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
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>
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>
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>
46 #include <linux/adb.h>
47 #include <linux/cuda.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
{
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
{
83 /* struct fb_fix_screeninfo fix;
84 struct fb_var_screeninfo var;*/
85 struct display display
;
86 struct fb_par_control par
;
88 __u8 red
, green
, blue
;
91 struct cmap_regs
*cmap_regs
;
92 unsigned long cmap_regs_phys
;
94 struct control_regs
*control_regs
;
95 unsigned long control_regs_phys
;
98 unsigned long frame_buffer_phys
;
100 int sense
, control_use_bank2
;
101 unsigned long total_vram
;
103 #ifdef FBCON_HAS_CFB16
106 #ifdef FBCON_HAS_CFB32
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
);
125 static int control_mmap(struct fb_info
*info
, struct file
*file
,
126 struct vm_area_struct
*vma
);
129 static int controlfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
,
130 u_int
*blue
, u_int
*transp
, struct fb_info
*info
);
131 static int controlfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
132 u_int transp
, struct fb_info
*info
);
134 /******************** Prototypes for internal functions ********************/
135 static void control_par_to_fix(struct fb_par_control
*par
, struct fb_fix_screeninfo
*fix
,
136 struct fb_info_control
*p
);
137 static void do_install_cmap(int con
, struct fb_info
*info
);
138 static void control_set_dispsw(struct display
*disp
, int cmode
, struct fb_info_control
*p
);
140 /************************* Internal variables *****************************/
141 static int currcon
= 0;
142 static int par_set
= 0;
143 static char fontname
[40] __initdata
= { 0 };
144 static int default_vmode
= VMODE_NVRAM
;
145 static int default_cmode
= CMODE_NVRAM
;
150 int control_init(void);
151 void control_setup(char *);
153 static void control_of_init(struct device_node
*dp
);
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
= {
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
,
176 fb_mmap
: control_mmap
,
181 /******************** The functions for controlfb_ops ********************/
184 int init_module(void)
186 struct device_node
*dp
;
188 printk("Loading...\n");
189 dp
= find_devices("control");
197 void cleanup_module(void)
199 /* FIXME: clean up and release regions */
203 /*********** Providing our information to the user ************/
205 static int control_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
206 struct fb_info
*info
)
208 struct fb_info_control
*p
= (struct fb_info_control
*) info
;
211 printk(KERN_ERR
"control_get_fix called with unset par!\n");
213 control_par_to_fix(&p
->par
, fix
, p
);
215 struct fb_par_control par
;
217 control_var_to_par(&fb_display
[con
].var
, &par
, info
);
218 control_par_to_fix(&par
, fix
, p
);
223 static int control_get_var(struct fb_var_screeninfo
*var
, int con
,
224 struct fb_info
*info
)
226 struct fb_info_control
*p
= (struct fb_info_control
*) info
;
229 printk(KERN_ERR
"control_get_var called with unset par!\n");
231 control_par_to_var(&p
->par
, var
);
233 *var
= fb_display
[con
].var
;
238 /* Sets everything according to var */
239 /* No longer safe for use in console switching */
240 static int control_set_var(struct fb_var_screeninfo
*var
, int con
,
241 struct fb_info
*info
)
243 struct fb_info_control
*p
= (struct fb_info_control
*) info
;
244 struct display
*disp
;
245 struct fb_par_control par
;
246 int depthchange
, err
;
247 int activate
= var
->activate
;
249 disp
= (con
>= 0) ? &fb_display
[con
] : info
->disp
;
251 if((err
= control_var_to_par(var
, &par
, info
))) {
252 printk (KERN_ERR
"control_set_var: error calling control_var_to_par: %d.\n", err
);
256 control_par_to_var(&par
, var
);
258 if ((activate
& FB_ACTIVATE_MASK
) != FB_ACTIVATE_NOW
)
261 /* I know, we want to use fb_display[con], but grab certain info from p->var instead. */
262 /* [above no longer true] */
263 depthchange
= (disp
->var
.bits_per_pixel
!= var
->bits_per_pixel
);
264 if(!VAR_MATCH(&disp
->var
, var
)) {
265 struct fb_fix_screeninfo fix
;
266 control_par_to_fix(&par
, &fix
, p
);
267 control_par_to_display(&par
, disp
, &fix
, p
);
269 (*info
->changevar
)(con
);
275 if(con
== currcon
|| con
== -1) {
276 control_set_hardware(p
, &par
);
279 if((err
= fb_alloc_cmap(&disp
->cmap
, 0, 0)))
281 do_install_cmap(con
, info
);
286 static int control_pan_display(struct fb_var_screeninfo
*var
, int con
,
287 struct fb_info
*info
)
289 struct fb_info_control
*p
= (struct fb_info_control
*)info
;
290 struct fb_par_control
*par
= &p
->par
;
292 if (var
->xoffset
!= 0 || var
->yoffset
+var
->yres
> var
->yres_virtual
)
294 fb_display
[con
].var
.yoffset
= par
->yoffset
= var
->yoffset
;
296 out_le32(&p
->control_regs
->start_addr
.r
,
297 par
->yoffset
* (par
->vxres
<< par
->cmode
));
301 static int control_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
302 struct fb_info
*info
)
304 if (con
== currcon
) /* current console? */
305 return fb_get_cmap(cmap
, kspc
, controlfb_getcolreg
, info
);
306 if (fb_display
[con
].cmap
.len
) /* non default colormap? */
307 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0: 2);
309 int size
= fb_display
[con
].var
.bits_per_pixel
== 16 ? 32 : 256;
310 fb_copy_cmap(fb_default_cmap(size
), cmap
, kspc
? 0 : 2);
315 static int control_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
316 struct fb_info
*info
)
318 struct display
*disp
= &fb_display
[con
];
321 if (disp
->cmap
.len
== 0) {
322 int size
= disp
->var
.bits_per_pixel
== 16 ? 32 : 256;
323 err
= fb_alloc_cmap(&disp
->cmap
, size
, 0);
328 return fb_set_cmap(cmap
, kspc
, controlfb_setcolreg
, info
);
329 fb_copy_cmap(cmap
, &disp
->cmap
, kspc
? 0 : 1);
333 /* Private mmap since we want to have a different caching on the framebuffer
335 * Note there's no locking in here; it's done in fb_mmap() in fbmem.c.
337 static int control_mmap(struct fb_info
*info
, struct file
*file
,
338 struct vm_area_struct
*vma
)
340 struct fb_ops
*fb
= info
->fbops
;
341 struct fb_fix_screeninfo fix
;
342 struct fb_var_screeninfo var
;
343 unsigned long off
, start
;
346 fb
->fb_get_fix(&fix
, PROC_CONSOLE(info
), info
);
347 off
= vma
->vm_pgoff
<< PAGE_SHIFT
;
349 /* frame buffer memory */
350 start
= fix
.smem_start
;
351 len
= PAGE_ALIGN((start
& ~PAGE_MASK
)+fix
.smem_len
);
353 /* memory mapped io */
355 fb
->fb_get_var(&var
, PROC_CONSOLE(info
), info
);
358 start
= fix
.mmio_start
;
359 len
= PAGE_ALIGN((start
& ~PAGE_MASK
)+fix
.mmio_len
);
360 pgprot_val(vma
->vm_page_prot
) |= _PAGE_NO_CACHE
|_PAGE_GUARDED
;
363 pgprot_val(vma
->vm_page_prot
) |= _PAGE_WRITETHRU
;
366 vma
->vm_pgoff
= off
>> PAGE_SHIFT
;
367 if (io_remap_page_range(vma
->vm_start
, off
,
368 vma
->vm_end
- vma
->vm_start
, vma
->vm_page_prot
))
375 /******************** End of controlfb_ops implementation ********************/
376 /* (new one that is) */
379 static int controlfb_switch(int con
, struct fb_info
*info
)
381 struct fb_info_control
*p
= (struct fb_info_control
*)info
;
382 struct fb_par_control par
;
383 int oldcon
= currcon
;
385 if (fb_display
[currcon
].cmap
.len
)
386 fb_get_cmap(&fb_display
[currcon
].cmap
, 1, controlfb_getcolreg
,
390 control_var_to_par(&fb_display
[con
].var
, &par
, info
);
391 control_set_hardware(p
, &par
);
392 control_set_dispsw(&fb_display
[con
], par
.cmode
, p
);
394 if(fb_display
[oldcon
].var
.yoffset
!= fb_display
[con
].var
.yoffset
)
395 controlfb_updatevar(con
, info
);
397 do_install_cmap(con
, info
);
401 static int controlfb_updatevar(int con
, struct fb_info
*info
)
403 struct fb_info_control
*p
= (struct fb_info_control
*)info
;
407 /* imsttfb blanks the unused bottom of the screen here...hmm. */
408 out_le32(&p
->control_regs
->start_addr
.r
,
409 fb_display
[con
].var
.yoffset
* fb_display
[con
].line_length
);
414 static void controlfb_blank(int blank_mode
, struct fb_info
*info
)
417 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
418 * then the caller blanks by setting the CLUT (Color Look Up Table) to all
419 * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
420 * to e.g. a video mode which doesn't support it. Implements VESA suspend
421 * and powerdown modes on hardware that supports disabling hsync/vsync:
422 * blank_mode == 2: suspend vsync
423 * blank_mode == 3: suspend hsync
424 * blank_mode == 4: powerdown
426 /* A blank_mode of 1+VESA_NO_BLANKING or 1+VESA_POWERDOWN act alike... */
427 struct fb_info_control
*p
= (struct fb_info_control
*) info
;
430 if(blank_mode
== 1+VESA_NO_BLANKING
)
431 blank_mode
= 1+VESA_POWERDOWN
;
432 ctrl
= ld_le32(&p
->control_regs
->ctrl
.r
) | 0x33;
435 if (blank_mode
& VESA_VSYNC_SUSPEND
)
437 if (blank_mode
& VESA_HSYNC_SUSPEND
)
439 out_le32(&p
->control_regs
->ctrl
.r
, ctrl
);
441 /* TODO: Figure out how the heck to powerdown this thing! */
446 static int controlfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
,
447 u_int
*blue
, u_int
*transp
, struct fb_info
*info
)
449 struct fb_info_control
*p
= (struct fb_info_control
*) info
;
453 *red
= (p
->palette
[regno
].red
<<8) | p
->palette
[regno
].red
;
454 *green
= (p
->palette
[regno
].green
<<8) | p
->palette
[regno
].green
;
455 *blue
= (p
->palette
[regno
].blue
<<8) | p
->palette
[regno
].blue
;
460 static int controlfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
461 u_int transp
, struct fb_info
*info
)
463 struct fb_info_control
*p
= (struct fb_info_control
*) info
;
474 p
->palette
[regno
].red
= r
;
475 p
->palette
[regno
].green
= g
;
476 p
->palette
[regno
].blue
= b
;
478 out_8(&p
->cmap_regs
->addr
, regno
); /* tell clut what addr to fill */
479 out_8(&p
->cmap_regs
->lut
, r
); /* send one color channel at */
480 out_8(&p
->cmap_regs
->lut
, g
); /* a time... */
481 out_8(&p
->cmap_regs
->lut
, b
);
484 switch (p
->par
.cmode
) {
485 #ifdef FBCON_HAS_CFB16
487 p
->fbcon_cmap
.cfb16
[regno
] = (regno
<< 10) | (regno
<< 5) | regno
;
490 #ifdef FBCON_HAS_CFB32
492 i
= (regno
<< 8) | regno
;
493 p
->fbcon_cmap
.cfb32
[regno
] = (i
<< 16) | i
;
500 static void do_install_cmap(int con
, struct fb_info
*info
)
504 if (fb_display
[con
].cmap
.len
)
505 fb_set_cmap(&fb_display
[con
].cmap
, 1, controlfb_setcolreg
,
508 int size
= fb_display
[con
].var
.bits_per_pixel
== 16 ? 32 : 256;
509 fb_set_cmap(fb_default_cmap(size
), 1, controlfb_setcolreg
,
514 static inline int control_vram_reqd(int video_mode
, int color_mode
)
516 return (control_reg_init
[video_mode
-1]->vres
517 * control_reg_init
[video_mode
-1]->hres
<< color_mode
)
518 + control_reg_init
[video_mode
-1]->offset
[color_mode
];
521 static void set_control_clock(unsigned char *params
)
523 struct adb_request req
;
526 #ifdef CONFIG_ADB_CUDA
527 for (i
= 0; i
< 3; ++i
) {
528 cuda_request(&req
, NULL
, 5, CUDA_PACKET
, CUDA_GET_SET_IIC
,
529 0x50, i
+ 1, params
[i
]);
530 while (!req
.complete
)
537 static void __init
init_control(struct fb_info_control
*p
)
539 struct fb_par_control parstruct
;
540 struct fb_par_control
*par
= &parstruct
;
541 struct fb_var_screeninfo var
;
543 p
->sense
= read_control_sense(p
);
544 printk(KERN_INFO
"Monitor sense value = 0x%x, ", p
->sense
);
545 /* Try to pick a video mode out of NVRAM if we have one. */
546 if (default_vmode
== VMODE_NVRAM
) {
547 par
->vmode
= nvram_read_byte(NV_VMODE
);
548 if(par
->vmode
<= 0 || par
->vmode
> VMODE_MAX
|| !control_reg_init
[par
->vmode
- 1])
549 par
->vmode
= VMODE_CHOOSE
;
550 if(par
->vmode
== VMODE_CHOOSE
)
551 par
->vmode
= mac_map_monitor_sense(p
->sense
);
552 if(!control_reg_init
[par
->vmode
- 1])
553 par
->vmode
= VMODE_640_480_60
;
555 par
->vmode
=default_vmode
;
557 if (default_cmode
== CMODE_NVRAM
){
558 par
->cmode
= nvram_read_byte(NV_CMODE
);
559 if(par
->cmode
< CMODE_8
|| par
->cmode
> CMODE_32
)
560 par
->cmode
= CMODE_8
;}
562 par
->cmode
=default_cmode
;
564 * Reduce the pixel size if we don't have enough VRAM.
566 while(par
->cmode
> CMODE_8
&& control_vram_reqd(par
->vmode
, par
->cmode
) > p
->total_vram
)
569 printk("using video mode %d and color mode %d.\n", par
->vmode
, par
->cmode
);
571 par
->vxres
= par
->xres
= control_reg_init
[par
->vmode
- 1]->hres
;
572 par
->yres
= control_reg_init
[par
->vmode
- 1]->vres
;
573 par
->vyres
= p
->total_vram
/ (par
->vxres
<< par
->cmode
);
574 par
->xoffset
= par
->yoffset
= 0;
576 control_init_info(&p
->info
, p
);
578 par_set
= 1; /* Debug */
580 control_par_to_var(par
, &var
);
581 var
.activate
= FB_ACTIVATE_NOW
;
582 control_set_var(&var
, -1, &p
->info
);
584 p
->info
.flags
= FBINFO_FLAG_DEFAULT
;
585 if (register_framebuffer(&p
->info
) < 0) {
590 printk(KERN_INFO
"fb%d: control display adapter\n", GET_FB_IDX(p
->info
.node
));
593 #define STORE_D2(a,d) \
594 out_8(&p->cmap_regs->addr, (a)); \
595 out_8(&p->cmap_regs->d2, (d))
597 /* Now how about actually saying, Make it so! */
598 /* Some things in here probably don't need to be done each time. */
599 static void control_set_hardware(struct fb_info_control
*p
, struct fb_par_control
*par
)
601 struct control_regvals
*init
;
602 volatile struct preg
*rp
;
606 if(PAR_EQUAL(&p
->par
, par
))
611 vmode
= p
->par
.vmode
;
612 cmode
= p
->par
.cmode
;
614 init
= control_reg_init
[vmode
- 1];
616 if (control_vram_reqd(vmode
, cmode
) > 0x200000)
618 else if (p
->control_use_bank2
)
622 if (vmode
>= VMODE_1280_960_75
&& cmode
>= CMODE_16
)
627 /* Initialize display timing registers */
628 out_le32(&p
->control_regs
->ctrl
.r
, 0x43b);
630 set_control_clock(init
->clock_params
);
632 STORE_D2(0x20, init
->radacal_ctrl
[cmode
]);
633 STORE_D2(0x21, p
->control_use_bank2
? 0 : 1);
637 rp
= &p
->control_regs
->vswin
;
638 for (i
= 0; i
< 16; ++i
, ++rp
)
639 out_le32(&rp
->r
, init
->regs
[i
]);
641 out_le32(&p
->control_regs
->pitch
.r
, par
->vxres
<< cmode
);
642 out_le32(&p
->control_regs
->mode
.r
, init
->mode
[cmode
]);
643 out_le32(&p
->control_regs
->flags
.r
, flags
);
644 out_le32(&p
->control_regs
->start_addr
.r
,
645 par
->yoffset
* (par
->vxres
<< cmode
));
646 out_le32(&p
->control_regs
->reg18
.r
, 0x1e5);
647 out_le32(&p
->control_regs
->reg19
.r
, 0);
649 for (i
= 0; i
< 16; ++i
) {
650 controlfb_setcolreg(color_table
[i
], default_red
[i
]<<8,
651 default_grn
[i
]<<8, default_blu
[i
]<<8,
652 0, (struct fb_info
*)p
);
654 /* Does the above need to be here each time? -- danj */
656 /* Turn on display */
657 out_le32(&p
->control_regs
->ctrl
.r
, ctrl
);
659 #ifdef CONFIG_FB_COMPAT_XPMAC
660 /* And let the world know the truth. */
661 if (!console_fb_info
|| console_fb_info
== &p
->info
) {
662 display_info
.height
= p
->par
.yres
;
663 display_info
.width
= p
->par
.xres
;
664 display_info
.depth
= (cmode
== CMODE_32
) ? 32 :
665 ((cmode
== CMODE_16
) ? 16 : 8);
666 display_info
.pitch
= p
->par
.vxres
<< p
->par
.cmode
;
667 display_info
.mode
= vmode
;
668 strncpy(display_info
.name
, "control",
669 sizeof(display_info
.name
));
670 display_info
.fb_address
= p
->frame_buffer_phys
671 + control_reg_init
[vmode
-1]->offset
[cmode
];
672 display_info
.cmap_adr_address
= p
->cmap_regs_phys
;
673 display_info
.cmap_data_address
= p
->cmap_regs_phys
+ 0x30;
674 display_info
.disp_reg_address
= p
->control_regs_phys
;
675 console_fb_info
= &p
->info
;
677 #endif /* CONFIG_FB_COMPAT_XPMAC */
680 int __init
control_init(void)
682 struct device_node
*dp
;
684 dp
= find_devices("control");
690 static void __init
control_of_init(struct device_node
*dp
)
692 struct fb_info_control
*p
;
693 unsigned long addr
, size
;
696 if(dp
->n_addrs
!= 2) {
697 printk(KERN_ERR
"expecting 2 address for control (got %d)", dp
->n_addrs
);
700 p
= kmalloc(sizeof(*p
), GFP_ATOMIC
);
703 memset(p
, 0, sizeof(*p
));
705 /* Map in frame buffer and registers */
706 for (i
= 0; i
< dp
->n_addrs
; ++i
) {
707 addr
= dp
->addrs
[i
].address
;
708 size
= dp
->addrs
[i
].size
;
709 /* Let's assume we can request either all or nothing */
710 if (!request_mem_region(addr
, size
, "controlfb")) {
714 if (size
>= 0x800000) {
715 /* use the big-endian aperture (??) */
717 /* map at most 8MB for the frame buffer */
718 p
->frame_buffer_phys
= addr
;
719 p
->frame_buffer
= __ioremap(addr
, 0x800000, _PAGE_WRITETHRU
);
721 p
->control_regs_phys
= addr
;
722 p
->control_regs
= ioremap(addr
, size
);
725 p
->cmap_regs_phys
= 0xf301b000; /* XXX not in prom? */
726 request_mem_region(p
->cmap_regs_phys
, 0x1000, "controlfb cmap");
727 p
->cmap_regs
= ioremap(p
->cmap_regs_phys
, 0x1000);
729 /* Work out which banks of VRAM we have installed. */
730 /* According to Andrew Fyfe <bandr@best.com>, the VRAM behaves like so: */
731 /* afyfe: observations from an 8500:
732 * - with 2M vram in bank 1, it appears at offsets 0, 2M and 4M
733 * - with 2M vram in bank 2, it appears only at offset 6M
734 * - with 4M vram, it appears only as a 4M block at offset 0.
737 /* We know there is something at 2M if there is something at 0M. */
738 out_8(&p
->frame_buffer
[0x200000], 0xa5);
739 out_8(&p
->frame_buffer
[0x200001], 0x38);
740 asm volatile("eieio; dcbi 0,%0" : : "r" (&p
->frame_buffer
[0x200000]) : "memory" );
742 out_8(&p
->frame_buffer
[0], 0x5a);
743 out_8(&p
->frame_buffer
[1], 0xc7);
744 asm volatile("eieio; dcbi 0,%0" : : "r" (&p
->frame_buffer
[0]) : "memory" );
746 bank1
= (in_8(&p
->frame_buffer
[0x000000]) == 0x5a)
747 && (in_8(&p
->frame_buffer
[0x000001]) == 0xc7);
748 bank2
= (in_8(&p
->frame_buffer
[0x200000]) == 0xa5)
749 && (in_8(&p
->frame_buffer
[0x200001]) == 0x38);
752 printk(KERN_INFO
"controlfb: Found memory at 2MB but not at 0! Please contact dan@debian.org\n");
755 out_8(&p
->frame_buffer
[0x600000], 0xa5);
756 out_8(&p
->frame_buffer
[0x600001], 0x38);
757 asm volatile("eieio; dcbi 0,%0" : : "r" (&p
->frame_buffer
[0x600000]) : "memory" );
758 bank2
= (in_8(&p
->frame_buffer
[0x600000]) == 0xa5)
759 && (in_8(&p
->frame_buffer
[0x600001]) == 0x38);
760 /* If we don't have bank 1 installed, we hope we have bank 2 :-) */
761 p
->control_use_bank2
= 1;
762 p
->frame_buffer
+= 0x600000;
763 p
->frame_buffer_phys
+= 0x600000;
766 p
->total_vram
= (bank1
+ bank2
) * 0x200000;
768 printk(KERN_INFO
"controlfb: Memory bank 1 %s, bank 2 %s, total VRAM %dMB\n",
769 bank1
? "present" : "absent", bank2
? "present" : "absent",
770 2 * (bank1
+ bank2
));
776 * Get the monitor sense value.
777 * Note that this can be called before calibrate_delay,
778 * so we can't use udelay.
780 static int read_control_sense(struct fb_info_control
*p
)
784 out_le32(&p
->control_regs
->mon_sense
.r
, 7); /* drive all lines high */
786 out_le32(&p
->control_regs
->mon_sense
.r
, 077); /* turn off drivers */
788 sense
= (in_le32(&p
->control_regs
->mon_sense
.r
) & 0x1c0) << 2;
790 /* drive each sense line low in turn and collect the other 2 */
791 out_le32(&p
->control_regs
->mon_sense
.r
, 033); /* drive A low */
793 sense
|= (in_le32(&p
->control_regs
->mon_sense
.r
) & 0xc0) >> 2;
794 out_le32(&p
->control_regs
->mon_sense
.r
, 055); /* drive B low */
796 sense
|= ((in_le32(&p
->control_regs
->mon_sense
.r
) & 0x100) >> 5)
797 | ((in_le32(&p
->control_regs
->mon_sense
.r
) & 0x40) >> 4);
798 out_le32(&p
->control_regs
->mon_sense
.r
, 066); /* drive C low */
800 sense
|= (in_le32(&p
->control_regs
->mon_sense
.r
) & 0x180) >> 7;
802 out_le32(&p
->control_regs
->mon_sense
.r
, 077); /* turn off drivers */
807 /*********************** Various translation functions ***********************/
809 /* This routine takes a user-supplied var, and picks the best vmode/cmode from it. */
810 static int control_var_to_par(struct fb_var_screeninfo
*var
,
811 struct fb_par_control
*par
, const struct fb_info
*fb_info
)
813 int xres
= var
->xres
;
814 int yres
= var
->yres
;
815 int bpp
= var
->bits_per_pixel
;
816 struct fb_info_control
*p
= (struct fb_info_control
*) fb_info
;
819 * Get the video params out of 'var'. If a value doesn't fit, round it up,
820 * if it's too big, return -EINVAL.
822 * Suggestion: Round up in the following order: bits_per_pixel, xres,
823 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
824 * bitfields, horizontal timing, vertical timing.
826 /* swiped by jonh from atyfb.c */
827 if (xres
<= 640 && yres
<= 480)
828 par
->vmode
= VMODE_640_480_67
; /* 640x480, 67Hz */
829 else if (xres
<= 640 && yres
<= 870)
830 par
->vmode
= VMODE_640_870_75P
; /* 640x870, 75Hz (portrait) */
831 else if (xres
<= 800 && yres
<= 600)
832 par
->vmode
= VMODE_800_600_75
; /* 800x600, 75Hz */
833 else if (xres
<= 832 && yres
<= 624)
834 par
->vmode
= VMODE_832_624_75
; /* 832x624, 75Hz */
835 else if (xres
<= 1024 && yres
<= 768)
836 par
->vmode
= VMODE_1024_768_75
; /* 1024x768, 75Hz */
837 else if (xres
<= 1152 && yres
<= 870)
838 par
->vmode
= VMODE_1152_870_75
; /* 1152x870, 75Hz */
839 else if (xres
<= 1280 && yres
<= 960)
840 par
->vmode
= VMODE_1280_960_75
; /* 1280x960, 75Hz */
841 else if (xres
<= 1280 && yres
<= 1024)
842 par
->vmode
= VMODE_1280_1024_75
; /* 1280x1024, 75Hz */
844 printk(KERN_ERR
"Bad x/y res in var_to_par\n");
848 xres
= control_reg_init
[par
->vmode
-1]->hres
;
849 yres
= control_reg_init
[par
->vmode
-1]->vres
;
854 if (var
->xres_virtual
<= xres
)
856 else if(var
->xres_virtual
> xres
) {
858 } else /* NotReached at present */
859 par
->vxres
= (var
->xres_virtual
+7) & ~7;
861 if (var
->yres_virtual
<= yres
)
864 par
->vyres
= var
->yres_virtual
;
866 if (var
->xoffset
> 0 || var
->yoffset
+yres
> par
->vyres
) {
867 printk(KERN_ERR
"Bad offsets in var_to_par\n");
871 par
->xoffset
= (var
->xoffset
+7) & ~7;
872 par
->yoffset
= var
->yoffset
;
876 par
->cmode
= CMODE_8
;
878 par
->cmode
= CMODE_16
;
880 par
->cmode
= CMODE_32
;
882 printk(KERN_ERR
"Bad bpp in var_to_par\n");
886 if (control_vram_reqd(par
->vmode
, par
->cmode
) > p
->total_vram
) {
887 printk(KERN_ERR
"Too much VRAM required for vmode %d cmode %d.\n", par
->vmode
, par
->cmode
);
891 /* Check if we know about the wanted video mode */
892 if (control_reg_init
[par
->vmode
- 1] == NULL
) {
893 printk(KERN_ERR
"init is null in control_var_to_par().\n");
894 /* I'm not sure if control has any specific requirements -- */
895 /* if we have a regvals struct, we're good to go? */
902 /* This routine takes a user-supplied var, and picks the best vmode/cmode from it. */
903 static int control_var_to_par(struct fb_var_screeninfo
*var
,
904 struct fb_par_control
*par
, const struct fb_info
*fb_info
)
906 struct fb_info_control
*p
= (struct fb_info_control
*) fb_info
;
908 if(mac_var_to_vmode(var
, &par
->vmode
, &par
->cmode
) != 0)
910 par
->xres
= par
->vxres
= vmode_attrs
[par
->vmode
- 1].hres
;
911 par
->yres
= par
->vyres
= vmode_attrs
[par
->vmode
- 1].vres
;
912 par
->xoffset
= par
->yoffset
= 0;
914 if (control_vram_reqd(par
->vmode
, par
->cmode
) > p
->total_vram
)
917 /* Check if we know about the wanted video mode */
918 if(!control_reg_init
[par
->vmode
-1]) {
919 /* I'm not sure if control has any specific requirements -- */
920 /* if we have a regvals struct, we're good to go? */
927 /*********** Convert hardware data in par to an fb_var_screeninfo ***********/
929 static void control_par_to_var(struct fb_par_control
*par
, struct fb_var_screeninfo
*var
)
931 struct control_regints
*rv
;
933 rv
= (struct control_regints
*) control_reg_init
[par
->vmode
- 1]->regs
;
935 memset(var
, 0, sizeof(*var
));
936 var
->xres
= control_reg_init
[par
->vmode
- 1]->hres
;
937 var
->yres
= control_reg_init
[par
->vmode
- 1]->vres
;
938 var
->xres_virtual
= par
->vxres
;
939 var
->yres_virtual
= par
->vyres
;
940 var
->xoffset
= par
->xoffset
;
941 var
->yoffset
= par
->yoffset
;
944 if(par
->cmode
!= CMODE_8
&& par
->cmode
!= CMODE_16
&& par
->cmode
!= CMODE_32
) {
945 printk(KERN_ERR
"Bad color mode in control_par_to_var()!\n");
946 par
->cmode
= CMODE_8
;
950 var
->bits_per_pixel
= 8;
953 var
->green
.offset
= 0;
954 var
->green
.length
= 8;
955 var
->blue
.offset
= 0;
956 var
->blue
.length
= 8;
957 var
->transp
.offset
= 0;
958 var
->transp
.length
= 0;
960 case CMODE_16
: /* RGB 555 */
961 var
->bits_per_pixel
= 16;
962 var
->red
.offset
= 10;
964 var
->green
.offset
= 5;
965 var
->green
.length
= 5;
966 var
->blue
.offset
= 0;
967 var
->blue
.length
= 5;
968 var
->transp
.offset
= 0;
969 var
->transp
.length
= 0;
971 case CMODE_32
: /* RGB 888 */
972 var
->bits_per_pixel
= 32;
973 var
->red
.offset
= 16;
975 var
->green
.offset
= 8;
976 var
->green
.length
= 8;
977 var
->blue
.offset
= 0;
978 var
->blue
.length
= 8;
979 var
->transp
.offset
= 24;
980 var
->transp
.length
= 8;
983 var
->red
.msb_right
= 0;
984 var
->green
.msb_right
= 0;
985 var
->blue
.msb_right
= 0;
986 var
->transp
.msb_right
= 0;
991 var
->vmode
= FB_VMODE_NONINTERLACED
;
993 var
->left_margin
= (rv
->heblank
- rv
->hesync
)
994 << ((par
->vmode
> 18) ? 2 : 1);
995 var
->right_margin
= (rv
->hssync
- rv
->hsblank
)
996 << ((par
->vmode
> 18) ? 2 : 1);
997 var
->hsync_len
= (rv
->hperiod
+ 2 - rv
->hssync
+ rv
->hesync
)
998 << ((par
->vmode
> 18) ? 2 : 1);
1000 var
->upper_margin
= (rv
->veblank
- rv
->vesync
) >> 1;
1001 var
->lower_margin
= (rv
->vssync
- rv
->vsblank
) >> 1;
1002 var
->vsync_len
= (rv
->vperiod
- rv
->vssync
+ rv
->vesync
) >> 1;
1004 /* Acording to macmodes.c... */
1005 if((par
->vmode
>= 9 && par
->vmode
<= 12) ||
1006 (par
->vmode
>= 16 && par
->vmode
<= 18) ||
1009 var
->sync
= FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
;
1011 var
->sync
= 0; /* I suppose */
1014 /* The reason these are both here: with my revised margin calculations, */
1015 /* these SHOULD both give the same answer for each mode. Some day I */
1016 /* will sit down and check the rest. Works perfectly for vmode 13. */
1019 /* jonh's pixclocks...*/
1020 /* no long long support in the kernel :-( */
1021 /* this splittig trick will work if xres > 232 */
1022 var
->pixclock
= 1000000000/
1023 (var
->left_margin
+var
->xres
+var
->right_margin
+var
->hsync_len
);
1024 var
->pixclock
*= 1000;
1025 var
->pixclock
/= vmode_attrs
[par
->vmode
-1].vfreq
*
1026 (var
->upper_margin
+var
->yres
+var
->lower_margin
+var
->vsync_len
);
1029 /* 10^12 * clock_params[0] / (3906400 * clock_params[1] * 2^clock_params[2]) */
1030 /* (10^12 * clock_params[0] / (3906400 * clock_params[1])) >> clock_params[2] */
1031 /* (255990.17 * clock_params[0] / clock_params[1]) >> clock_params[2] */
1032 var
->pixclock
= 255990 * control_reg_init
[par
->vmode
-1]->clock_params
[0];
1033 var
->pixclock
/= control_reg_init
[par
->vmode
-1]->clock_params
[1];
1034 var
->pixclock
>>= control_reg_init
[par
->vmode
-1]->clock_params
[2];
1038 static void control_par_to_fix(struct fb_par_control
*par
, struct fb_fix_screeninfo
*fix
,
1039 struct fb_info_control
*p
)
1041 memset(fix
, 0, sizeof(*fix
));
1042 strcpy(fix
->id
, "control");
1043 fix
->mmio_start
= p
->control_regs_phys
;
1044 fix
->mmio_len
= sizeof(struct control_regs
);
1045 fix
->type
= FB_TYPE_PACKED_PIXELS
;
1055 fix
->smem_start
= (p
->frame_buffer_phys
1056 + control_reg_init
[par
->vmode
-1]->offset
[par
->cmode
]);
1057 fix
->smem_len
= p
->total_vram
- control_reg_init
[par
->vmode
-1]->offset
[par
->cmode
];
1058 fix
->visual
= (par
->cmode
== CMODE_8
) ?
1059 FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_DIRECTCOLOR
;
1060 fix
->line_length
= par
->vxres
<< par
->cmode
;
1063 /* We never initialize any display except for p->disp.
1064 And p->disp is already memset to 0. So no memset here.
1065 [Found by Takashi Oe]
1067 static void control_par_to_display(struct fb_par_control
*par
,
1068 struct display
*disp
, struct fb_fix_screeninfo
*fix
, struct fb_info_control
*p
)
1070 /* memset(disp, 0, sizeof(*disp)); */
1071 disp
->type
= fix
->type
;
1072 disp
->can_soft_blank
= 1;
1073 disp
->scrollmode
= SCROLL_YNOMOVE
| SCROLL_YNOPARTIAL
;
1074 disp
->ypanstep
= fix
->ypanstep
;
1075 disp
->ywrapstep
= fix
->ywrapstep
;
1077 disp
->type_aux
= fix
->type_aux
;
1078 disp
->cmap
.red
= NULL
; /* ??? danj */
1079 disp
->cmap
.green
= NULL
;
1080 disp
->cmap
.blue
= NULL
;
1081 disp
->cmap
.transp
= NULL
;
1082 /* Yeah, I realize I just set 0 = 0. */
1085 control_par_to_var(par
, &disp
->var
);
1086 disp
->screen_base
= (char *) p
->frame_buffer
1087 + control_reg_init
[par
->vmode
-1]->offset
[par
->cmode
];
1088 disp
->visual
= fix
->visual
;
1089 disp
->line_length
= fix
->line_length
;
1090 control_set_dispsw(disp
, par
->cmode
, p
);
1093 static void control_cfb16_revc(struct display
*p
, int xx
, int yy
)
1096 int bytes
= p
->next_line
, rows
;
1098 dest
= p
->screen_base
+ yy
* fontheight(p
) * bytes
+ xx
* fontwidth(p
)*2;
1099 for (rows
= fontheight(p
); rows
--; dest
+= bytes
) {
1100 switch (fontwidth(p
)) {
1102 ((u32
*)dest
)[6] ^= 0x3def3def; ((u32
*)dest
)[7] ^= 0x3def3def;
1105 ((u32
*)dest
)[4] ^= 0x3def3def; ((u32
*)dest
)[5] ^= 0x3def3def;
1108 ((u32
*)dest
)[2] ^= 0x3def3def; ((u32
*)dest
)[3] ^= 0x3def3def;
1111 ((u32
*)dest
)[0] ^= 0x3def3def; ((u32
*)dest
)[1] ^= 0x3def3def;
1116 static void control_cfb32_revc(struct display
*p
, int xx
, int yy
)
1119 int bytes
= p
->next_line
, rows
;
1121 dest
= p
->screen_base
+ yy
* fontheight(p
) * bytes
+ xx
* fontwidth(p
) * 4;
1122 for (rows
= fontheight(p
); rows
--; dest
+= bytes
) {
1123 switch (fontwidth(p
)) {
1125 ((u32
*)dest
)[12] ^= 0x0f0f0f0f; ((u32
*)dest
)[13] ^= 0x0f0f0f0f;
1126 ((u32
*)dest
)[14] ^= 0x0f0f0f0f; ((u32
*)dest
)[15] ^= 0x0f0f0f0f;
1129 ((u32
*)dest
)[8] ^= 0x0f0f0f0f; ((u32
*)dest
)[9] ^= 0x0f0f0f0f;
1130 ((u32
*)dest
)[10] ^= 0x0f0f0f0f; ((u32
*)dest
)[11] ^= 0x0f0f0f0f;
1133 ((u32
*)dest
)[4] ^= 0x0f0f0f0f; ((u32
*)dest
)[5] ^= 0x0f0f0f0f;
1134 ((u32
*)dest
)[6] ^= 0x0f0f0f0f; ((u32
*)dest
)[7] ^= 0x0f0f0f0f;
1137 ((u32
*)dest
)[0] ^= 0x0f0f0f0f; ((u32
*)dest
)[1] ^= 0x0f0f0f0f;
1138 ((u32
*)dest
)[2] ^= 0x0f0f0f0f; ((u32
*)dest
)[3] ^= 0x0f0f0f0f;
1144 static struct display_switch control_cfb16
= {
1145 setup
: fbcon_cfb16_setup
,
1146 bmove
: fbcon_cfb16_bmove
,
1147 clear
: fbcon_cfb16_clear
,
1148 putc
: fbcon_cfb16_putc
,
1149 putcs
: fbcon_cfb16_putcs
,
1150 revc
: control_cfb16_revc
,
1151 clear_margins
: fbcon_cfb16_clear_margins
,
1152 fontwidthmask
: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1155 static struct display_switch control_cfb32
= {
1156 setup
: fbcon_cfb32_setup
,
1157 bmove
: fbcon_cfb32_bmove
,
1158 clear
: fbcon_cfb32_clear
,
1159 putc
: fbcon_cfb32_putc
,
1160 putcs
: fbcon_cfb32_putcs
,
1161 revc
: control_cfb32_revc
,
1162 clear_margins
: fbcon_cfb32_clear_margins
,
1163 fontwidthmask
: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1167 static void control_set_dispsw(struct display
*disp
, int cmode
, struct fb_info_control
*p
)
1170 #ifdef FBCON_HAS_CFB8
1172 disp
->dispsw
= &fbcon_cfb8
;
1175 #ifdef FBCON_HAS_CFB16
1177 disp
->dispsw
= &control_cfb16
;
1178 disp
->dispsw_data
= p
->fbcon_cmap
.cfb16
;
1181 #ifdef FBCON_HAS_CFB32
1183 disp
->dispsw
= &control_cfb32
;
1184 disp
->dispsw_data
= p
->fbcon_cmap
.cfb32
;
1188 disp
->dispsw
= &fbcon_dummy
;
1193 static void __init
control_init_info(struct fb_info
*info
, struct fb_info_control
*p
)
1195 strcpy(info
->modename
, "control");
1196 info
->node
= -1; /* ??? danj */
1197 info
->fbops
= &controlfb_ops
;
1198 info
->disp
= &p
->display
;
1199 strcpy(info
->fontname
, fontname
);
1200 info
->changevar
= NULL
;
1201 info
->switch_con
= &controlfb_switch
;
1202 info
->updatevar
= &controlfb_updatevar
;
1203 info
->blank
= &controlfb_blank
;
1206 /* Parse user speficied options (`video=controlfb:') */
1207 void __init
control_setup(char *options
)
1211 if (!options
|| !*options
)
1214 for (this_opt
= strtok(options
, ","); this_opt
;
1215 this_opt
= strtok(NULL
, ",")) {
1216 if (!strncmp(this_opt
, "font:", 5)) {
1221 for (i
= 0; i
< sizeof(fontname
) - 1; i
++)
1222 if (!*p
|| *p
== ' ' || *p
== ',')
1224 memcpy(fontname
, this_opt
+ 5, i
);
1227 if (!strncmp(this_opt
, "vmode:", 6)) {
1228 int vmode
= simple_strtoul(this_opt
+6, NULL
, 0);
1229 if (vmode
> 0 && vmode
<= VMODE_MAX
)
1230 default_vmode
= vmode
;
1231 } else if (!strncmp(this_opt
, "cmode:", 6)) {
1232 int depth
= simple_strtoul(this_opt
+6, NULL
, 0);
1237 default_cmode
= depth
;
1240 default_cmode
= CMODE_8
;
1244 default_cmode
= CMODE_16
;
1248 default_cmode
= CMODE_32
;
1256 static int controlfb_pan_display(struct fb_var_screeninfo
*var
,
1257 struct controlfb_par
*par
,
1258 const struct fb_info
*fb_info
)
1261 * Pan (or wrap, depending on the `vmode' field) the display using the
1262 * `xoffset' and `yoffset' fields of the `var' structure.
1263 * If the values don't fit, return -EINVAL.