Import 2.3.18pre1
[davej-history.git] / drivers / video / vesafb.c
blobbb98bd8c730b44b43c2337db1fe9ff801d4ee6b5
1 /*
2 * framebuffer driver for VBE 2.0 compliant graphic boards
4 * switching to graphics mode happens at boot time (while
5 * running in real mode, see arch/i386/video.S).
7 * (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
9 */
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/errno.h>
14 #include <linux/string.h>
15 #include <linux/mm.h>
16 #include <linux/tty.h>
17 #include <linux/malloc.h>
18 #include <linux/delay.h>
19 #include <linux/fb.h>
20 #include <linux/console.h>
21 #include <linux/selection.h>
22 #include <linux/ioport.h>
23 #include <linux/init.h>
25 #include <asm/io.h>
26 #include <asm/mtrr.h>
28 #include <video/fbcon.h>
29 #include <video/fbcon-cfb8.h>
30 #include <video/fbcon-cfb16.h>
31 #include <video/fbcon-cfb24.h>
32 #include <video/fbcon-cfb32.h>
34 #define dac_reg (0x3c8)
35 #define dac_val (0x3c9)
37 /* --------------------------------------------------------------------- */
40 * card parameters
43 /* card */
44 unsigned long video_base; /* physical addr */
45 int video_size;
46 char *video_vbase; /* mapped */
48 /* mode */
49 int video_bpp;
50 int video_width;
51 int video_height;
52 int video_height_virtual;
53 int video_type = FB_TYPE_PACKED_PIXELS;
54 int video_visual;
55 int video_linelength;
56 int video_cmap_len;
58 /* --------------------------------------------------------------------- */
60 static struct fb_var_screeninfo vesafb_defined = {
61 0,0,0,0, /* W,H, W, H (virtual) load xres,xres_virtual*/
62 0,0, /* virtual -> visible no offset */
63 8, /* depth -> load bits_per_pixel */
64 0, /* greyscale ? */
65 {0,0,0}, /* R */
66 {0,0,0}, /* G */
67 {0,0,0}, /* B */
68 {0,0,0}, /* transparency */
69 0, /* standard pixel format */
70 FB_ACTIVATE_NOW,
71 -1,-1,
73 0L,0L,0L,0L,0L,
74 0L,0L,0, /* No sync info */
75 FB_VMODE_NONINTERLACED,
76 {0,0,0,0,0,0}
79 static struct display disp;
80 static struct fb_info fb_info;
81 static struct { u_short blue, green, red, pad; } palette[256];
82 static union {
83 #ifdef FBCON_HAS_CFB16
84 u16 cfb16[16];
85 #endif
86 #ifdef FBCON_HAS_CFB24
87 u32 cfb24[16];
88 #endif
89 #ifdef FBCON_HAS_CFB32
90 u32 cfb32[16];
91 #endif
92 } fbcon_cmap;
94 static int inverse = 0;
95 static int mtrr = 0;
96 static int currcon = 0;
98 static int pmi_setpal = 0; /* pmi for palette changes ??? */
99 static int ypan = 0; /* 0..nothing, 1..ypan, 2..ywrap */
100 static unsigned short *pmi_base = 0;
101 static void (*pmi_start)(void);
102 static void (*pmi_pal)(void);
104 static struct display_switch vesafb_sw;
106 /* --------------------------------------------------------------------- */
109 * Open/Release the frame buffer device
112 static int vesafb_open(struct fb_info *info, int user)
115 * Nothing, only a usage count for the moment
117 MOD_INC_USE_COUNT;
118 return(0);
121 static int vesafb_release(struct fb_info *info, int user)
123 MOD_DEC_USE_COUNT;
124 return(0);
127 static int vesafb_pan_display(struct fb_var_screeninfo *var, int con,
128 struct fb_info *info)
130 int offset;
132 if (!ypan)
133 return -EINVAL;
134 if (var->xoffset)
135 return -EINVAL;
136 if (var->yoffset > var->yres_virtual)
137 return -EINVAL;
138 if ((ypan==1) && var->yoffset+var->yres > var->yres_virtual)
139 return -EINVAL;
141 offset = (var->yoffset * video_linelength + var->xoffset) / 4;
143 __asm__ __volatile__(
144 "call *(%%edi)"
145 : /* no return value */
146 : "a" (0x4f07), /* EAX */
147 "b" (0), /* EBX */
148 "c" (offset), /* ECX */
149 "d" (offset >> 16), /* EDX */
150 "D" (&pmi_start)); /* EDI */
151 return 0;
154 static int vesafb_update_var(int con, struct fb_info *info)
156 if (con == currcon && ypan) {
157 struct fb_var_screeninfo *var = &fb_display[currcon].var;
158 return vesafb_pan_display(var,con,info);
160 return 0;
163 static int vesafb_get_fix(struct fb_fix_screeninfo *fix, int con,
164 struct fb_info *info)
166 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
167 strcpy(fix->id,"VESA VGA");
169 fix->smem_start=video_base;
170 fix->smem_len=video_size;
171 fix->type = video_type;
172 fix->visual = video_visual;
173 fix->xpanstep = 0;
174 fix->ypanstep = ypan ? 1 : 0;
175 fix->ywrapstep = (ypan>1) ? 1 : 0;
176 fix->line_length=video_linelength;
177 return 0;
180 static int vesafb_get_var(struct fb_var_screeninfo *var, int con,
181 struct fb_info *info)
183 if(con==-1)
184 memcpy(var, &vesafb_defined, sizeof(struct fb_var_screeninfo));
185 else
186 *var=fb_display[con].var;
187 return 0;
190 static void vesafb_set_disp(int con)
192 struct fb_fix_screeninfo fix;
193 struct display *display;
194 struct display_switch *sw;
196 if (con >= 0)
197 display = &fb_display[con];
198 else
199 display = &disp; /* used during initialization */
201 vesafb_get_fix(&fix, con, 0);
203 memset(display, 0, sizeof(struct display));
204 display->screen_base = video_vbase;
205 display->visual = fix.visual;
206 display->type = fix.type;
207 display->type_aux = fix.type_aux;
208 display->ypanstep = fix.ypanstep;
209 display->ywrapstep = fix.ywrapstep;
210 display->line_length = fix.line_length;
211 display->next_line = fix.line_length;
212 display->can_soft_blank = 0;
213 display->inverse = inverse;
214 vesafb_get_var(&display->var, -1, &fb_info);
216 switch (video_bpp) {
217 #ifdef FBCON_HAS_CFB8
218 case 8:
219 sw = &fbcon_cfb8;
220 break;
221 #endif
222 #ifdef FBCON_HAS_CFB16
223 case 15:
224 case 16:
225 sw = &fbcon_cfb16;
226 display->dispsw_data = fbcon_cmap.cfb16;
227 break;
228 #endif
229 #ifdef FBCON_HAS_CFB24
230 case 24:
231 sw = &fbcon_cfb24;
232 display->dispsw_data = fbcon_cmap.cfb24;
233 break;
234 #endif
235 #ifdef FBCON_HAS_CFB32
236 case 32:
237 sw = &fbcon_cfb32;
238 display->dispsw_data = fbcon_cmap.cfb32;
239 break;
240 #endif
241 default:
242 sw = &fbcon_dummy;
243 return;
245 memcpy(&vesafb_sw, sw, sizeof(*sw));
246 display->dispsw = &vesafb_sw;
247 if (!ypan) {
248 display->scrollmode = SCROLL_YREDRAW;
249 vesafb_sw.bmove = fbcon_redraw_bmove;
253 static int vesafb_set_var(struct fb_var_screeninfo *var, int con,
254 struct fb_info *info)
256 static int first = 1;
258 if (var->xres != vesafb_defined.xres ||
259 var->yres != vesafb_defined.yres ||
260 var->xres_virtual != vesafb_defined.xres_virtual ||
261 var->yres_virtual > video_height_virtual ||
262 var->yres_virtual < video_height ||
263 var->xoffset ||
264 var->bits_per_pixel != vesafb_defined.bits_per_pixel ||
265 var->nonstd) {
266 if (first) {
267 printk(KERN_ERR "Vesafb does not support changing the video mode\n");
268 first = 0;
270 return -EINVAL;
273 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
274 return 0;
276 if (ypan) {
277 if (vesafb_defined.yres_virtual != var->yres_virtual) {
278 vesafb_defined.yres_virtual = var->yres_virtual;
279 if (con != -1) {
280 fb_display[con].var = vesafb_defined;
281 info->changevar(con);
285 if (var->yoffset != vesafb_defined.yoffset)
286 return vesafb_pan_display(var,con,info);
287 return 0;
290 if (var->yoffset)
291 return -EINVAL;
292 return 0;
295 static int vesa_getcolreg(unsigned regno, unsigned *red, unsigned *green,
296 unsigned *blue, unsigned *transp,
297 struct fb_info *fb_info)
300 * Read a single color register and split it into colors/transparent.
301 * Return != 0 for invalid regno.
304 if (regno >= video_cmap_len)
305 return 1;
307 *red = palette[regno].red;
308 *green = palette[regno].green;
309 *blue = palette[regno].blue;
310 *transp = 0;
311 return 0;
314 #ifdef FBCON_HAS_CFB8
316 static void vesa_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
318 struct { u_char blue, green, red, pad; } entry;
320 if (pmi_setpal) {
321 entry.red = red >> 10;
322 entry.green = green >> 10;
323 entry.blue = blue >> 10;
324 entry.pad = 0;
325 __asm__ __volatile__(
326 "call *(%%esi)"
327 : /* no return value */
328 : "a" (0x4f09), /* EAX */
329 "b" (0), /* EBX */
330 "c" (1), /* ECX */
331 "d" (regno), /* EDX */
332 "D" (&entry), /* EDI */
333 "S" (&pmi_pal)); /* ESI */
334 } else {
335 /* without protected mode interface, try VGA registers... */
336 outb_p(regno, dac_reg);
337 outb_p(red >> 10, dac_val);
338 outb_p(green >> 10, dac_val);
339 outb_p(blue >> 10, dac_val);
343 #endif
345 static int vesa_setcolreg(unsigned regno, unsigned red, unsigned green,
346 unsigned blue, unsigned transp,
347 struct fb_info *fb_info)
350 * Set a single color register. The values supplied are
351 * already rounded down to the hardware's capabilities
352 * (according to the entries in the `var' structure). Return
353 * != 0 for invalid regno.
356 if (regno >= video_cmap_len)
357 return 1;
359 palette[regno].red = red;
360 palette[regno].green = green;
361 palette[regno].blue = blue;
363 switch (video_bpp) {
364 #ifdef FBCON_HAS_CFB8
365 case 8:
366 vesa_setpalette(regno,red,green,blue);
367 break;
368 #endif
369 #ifdef FBCON_HAS_CFB16
370 case 15:
371 case 16:
372 if (vesafb_defined.red.offset == 10) {
373 /* 1:5:5:5 */
374 fbcon_cmap.cfb16[regno] =
375 ((red & 0xf800) >> 1) |
376 ((green & 0xf800) >> 6) |
377 ((blue & 0xf800) >> 11);
378 } else {
379 /* 0:5:6:5 */
380 fbcon_cmap.cfb16[regno] =
381 ((red & 0xf800) ) |
382 ((green & 0xfc00) >> 5) |
383 ((blue & 0xf800) >> 11);
385 break;
386 #endif
387 #ifdef FBCON_HAS_CFB24
388 case 24:
389 red >>= 8;
390 green >>= 8;
391 blue >>= 8;
392 fbcon_cmap.cfb24[regno] =
393 (red << vesafb_defined.red.offset) |
394 (green << vesafb_defined.green.offset) |
395 (blue << vesafb_defined.blue.offset);
396 break;
397 #endif
398 #ifdef FBCON_HAS_CFB32
399 case 32:
400 red >>= 8;
401 green >>= 8;
402 blue >>= 8;
403 fbcon_cmap.cfb32[regno] =
404 (red << vesafb_defined.red.offset) |
405 (green << vesafb_defined.green.offset) |
406 (blue << vesafb_defined.blue.offset);
407 break;
408 #endif
410 return 0;
413 static void do_install_cmap(int con, struct fb_info *info)
415 if (con != currcon)
416 return;
417 if (fb_display[con].cmap.len)
418 fb_set_cmap(&fb_display[con].cmap, 1, vesa_setcolreg, info);
419 else
420 fb_set_cmap(fb_default_cmap(video_cmap_len), 1, vesa_setcolreg,
421 info);
424 static int vesafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
425 struct fb_info *info)
427 if (con == currcon) /* current console? */
428 return fb_get_cmap(cmap, kspc, vesa_getcolreg, info);
429 else if (fb_display[con].cmap.len) /* non default colormap? */
430 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
431 else
432 fb_copy_cmap(fb_default_cmap(video_cmap_len),
433 cmap, kspc ? 0 : 2);
434 return 0;
437 static int vesafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
438 struct fb_info *info)
440 int err;
442 if (!fb_display[con].cmap.len) { /* no colormap allocated? */
443 err = fb_alloc_cmap(&fb_display[con].cmap,video_cmap_len,0);
444 if (err)
445 return err;
447 if (con == currcon) /* current console? */
448 return fb_set_cmap(cmap, kspc, vesa_setcolreg, info);
449 else
450 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
451 return 0;
454 static int vesafb_ioctl(struct inode *inode, struct file *file,
455 unsigned int cmd, unsigned long arg, int con,
456 struct fb_info *info)
458 return -EINVAL;
461 static struct fb_ops vesafb_ops = {
462 vesafb_open,
463 vesafb_release,
464 vesafb_get_fix,
465 vesafb_get_var,
466 vesafb_set_var,
467 vesafb_get_cmap,
468 vesafb_set_cmap,
469 vesafb_pan_display,
470 vesafb_ioctl
473 int vesafb_setup(char *options)
475 char *this_opt;
477 fb_info.fontname[0] = '\0';
479 if (!options || !*options)
480 return 0;
482 for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
483 if (!*this_opt) continue;
485 if (! strcmp(this_opt, "inverse"))
486 inverse=1;
487 else if (! strcmp(this_opt, "redraw"))
488 ypan=0;
489 else if (! strcmp(this_opt, "ypan"))
490 ypan=1;
491 else if (! strcmp(this_opt, "ywrap"))
492 ypan=2;
493 else if (! strcmp(this_opt, "vgapal"))
494 pmi_setpal=0;
495 else if (! strcmp(this_opt, "pmipal"))
496 pmi_setpal=1;
497 else if (! strcmp(this_opt, "mtrr"))
498 mtrr=1;
499 else if (!strncmp(this_opt, "font:", 5))
500 strcpy(fb_info.fontname, this_opt+5);
502 return 0;
505 static int vesafb_switch(int con, struct fb_info *info)
507 /* Do we have to save the colormap? */
508 if (fb_display[currcon].cmap.len)
509 fb_get_cmap(&fb_display[currcon].cmap, 1, vesa_getcolreg,
510 info);
512 currcon = con;
513 /* Install new colormap */
514 do_install_cmap(con, info);
515 vesafb_update_var(con,info);
516 return 1;
519 /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
521 static void vesafb_blank(int blank, struct fb_info *info)
523 /* Not supported */
526 int __init vesafb_init(void)
528 int i,j;
530 if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB)
531 return -ENXIO;
533 video_base = screen_info.lfb_base;
534 video_bpp = screen_info.lfb_depth;
535 video_width = screen_info.lfb_width;
536 video_height = screen_info.lfb_height;
537 video_linelength = screen_info.lfb_linelength;
538 video_size = screen_info.lfb_size * 65536;
539 video_visual = (video_bpp == 8) ?
540 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
542 if (!__request_region(&iomem_resource, video_base, video_size,
543 "vesafb")) {
544 printk(KERN_ERR
545 "vesafb: abort, cannot reserve video memory at 0x%lu\n",
546 video_base);
547 return -1;
550 video_vbase = ioremap(video_base, video_size);
552 printk(KERN_INFO "vesafb: framebuffer at 0x%lu, mapped to 0x%p, size %dk\n",
553 video_base, video_vbase, video_size/1024);
554 printk(KERN_INFO "vesafb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
555 video_width, video_height, video_bpp, video_linelength, screen_info.pages);
557 if (screen_info.vesapm_seg) {
558 printk(KERN_INFO "vesafb: protected mode interface info at %04x:%04x\n",
559 screen_info.vesapm_seg,screen_info.vesapm_off);
562 if (screen_info.vesapm_seg < 0xc000)
563 ypan = pmi_setpal = 0; /* not available or some DOS TSR ... */
565 if (ypan || pmi_setpal) {
566 pmi_base = (unsigned short*)bus_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off);
567 pmi_start = (void*)((char*)pmi_base + pmi_base[1]);
568 pmi_pal = (void*)((char*)pmi_base + pmi_base[2]);
569 printk(KERN_INFO "vesafb: pmi: set display start = %p, set palette = %p\n",pmi_start,pmi_pal);
570 if (pmi_base[3]) {
571 printk(KERN_INFO "vesafb: pmi: ports = ");
572 for (i = pmi_base[3]/2; pmi_base[i] != 0xffff; i++)
573 printk("%x ",pmi_base[i]);
574 printk("\n");
575 if (pmi_base[i] != 0xffff) {
577 * memory areas not supported (yet?)
579 * Rules are: we have to set up a descriptor for the requested
580 * memory area and pass it in the ES register to the BIOS function.
582 printk(KERN_INFO "vesafb: can't handle memory requests, pmi disabled\n");
583 ypan = pmi_setpal = 0;
588 vesafb_defined.xres=video_width;
589 vesafb_defined.yres=video_height;
590 vesafb_defined.xres_virtual=video_width;
591 vesafb_defined.yres_virtual=video_size / video_linelength;
592 vesafb_defined.bits_per_pixel=video_bpp;
594 if (ypan && vesafb_defined.yres_virtual > video_height) {
595 printk(KERN_INFO "vesafb: scrolling: %s using protected mode interface, yres_virtual=%d\n",
596 (ypan > 1) ? "ywrap" : "ypan",vesafb_defined.yres_virtual);
597 } else {
598 printk(KERN_INFO "vesafb: scrolling: redraw\n");
599 vesafb_defined.yres_virtual = video_height;
600 ypan = 0;
602 video_height_virtual = vesafb_defined.yres_virtual;
604 /* some dummy values for timing to make fbset happy */
605 vesafb_defined.pixclock = 10000000 / video_width * 1000 / video_height;
606 vesafb_defined.left_margin = (video_width / 8) & 0xf8;
607 vesafb_defined.right_margin = 32;
608 vesafb_defined.upper_margin = 16;
609 vesafb_defined.lower_margin = 4;
610 vesafb_defined.hsync_len = (video_width / 8) & 0xf8;
611 vesafb_defined.vsync_len = 4;
613 if (video_bpp > 8) {
614 vesafb_defined.red.offset = screen_info.red_pos;
615 vesafb_defined.red.length = screen_info.red_size;
616 vesafb_defined.green.offset = screen_info.green_pos;
617 vesafb_defined.green.length = screen_info.green_size;
618 vesafb_defined.blue.offset = screen_info.blue_pos;
619 vesafb_defined.blue.length = screen_info.blue_size;
620 vesafb_defined.transp.offset = screen_info.rsvd_pos;
621 vesafb_defined.transp.length = screen_info.rsvd_size;
622 printk(KERN_INFO "vesafb: directcolor: "
623 "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
624 screen_info.rsvd_size,
625 screen_info.red_size,
626 screen_info.green_size,
627 screen_info.blue_size,
628 screen_info.rsvd_pos,
629 screen_info.red_pos,
630 screen_info.green_pos,
631 screen_info.blue_pos);
632 video_cmap_len = 16;
633 } else {
634 vesafb_defined.red.length = 6;
635 vesafb_defined.green.length = 6;
636 vesafb_defined.blue.length = 6;
637 for(i = 0; i < 16; i++) {
638 j = color_table[i];
639 palette[i].red = default_red[j];
640 palette[i].green = default_grn[j];
641 palette[i].blue = default_blu[j];
643 video_cmap_len = 256;
646 /* request failure does not faze us, as vgacon probably has this
647 * region already (FIXME) */
648 __request_region(&ioport_resource, 0x3c0, 32, "vesafb");
650 if (mtrr)
651 mtrr_add(video_base, video_size, MTRR_TYPE_WRCOMB, 1);
653 strcpy(fb_info.modename, "VESA VGA");
654 fb_info.changevar = NULL;
655 fb_info.node = -1;
656 fb_info.fbops = &vesafb_ops;
657 fb_info.disp=&disp;
658 fb_info.switch_con=&vesafb_switch;
659 fb_info.updatevar=&vesafb_update_var;
660 fb_info.blank=&vesafb_blank;
661 fb_info.flags=FBINFO_FLAG_DEFAULT;
662 vesafb_set_disp(-1);
664 if (register_framebuffer(&fb_info)<0)
665 return -EINVAL;
667 printk(KERN_INFO "fb%d: %s frame buffer device\n",
668 GET_FB_IDX(fb_info.node), fb_info.modename);
669 return 0;
673 * Overrides for Emacs so that we follow Linus's tabbing style.
674 * ---------------------------------------------------------------------------
675 * Local variables:
676 * c-basic-offset: 8
677 * End: