diff --git a/Documentation/fb/vesafb.txt b/Documentation/fb/vesafb.txt index ee277dd..93d6e6e 100644 --- a/Documentation/fb/vesafb.txt +++ b/Documentation/fb/vesafb.txt @@ -2,16 +2,18 @@ What is vesafb? =============== -This is a generic driver for a graphic framebuffer on intel boxes. +Vesafb is a generic framebuffer driver for x86 and x86_64 boxes. -The idea is simple: Turn on graphics mode at boot time with the help -of the BIOS, and use this as framebuffer device /dev/fb0, like the m68k -(and other) ports do. +VESA BIOS Extensions Version 2.0 are required, because we need access to +a linear frame buffer. VBE 3.0 is required if you want to use modes with a +higher (than the standard 60 Hz) refresh rate. -This means we decide at boot time whenever we want to run in text or -graphics mode. Switching mode later on (in protected mode) is -impossible; BIOS calls work in real mode only. VESA BIOS Extensions -Version 2.0 are required, because we need a linear frame buffer. +The VESA framebuffer driver comes in two flavors - the standard 'vesafb' +and 'vesafb-tng'. Vesafb-tng is available only on 32-bit x86 due to the +technology it uses (vm86). Vesafb-tng has more features than vesafb +(adjusting the refresh rate on VBE 3.0 compliant boards, switching the +video mode without rebooting, selecting a mode by providing its +modedb name, and more). Advantages: @@ -29,26 +31,35 @@ Disadvantages: How to use it? ============== -Switching modes is done using the vga=... boot parameter. Read -Documentation/svga.txt for details. +If you are running a 32-bit x86 system and you decide to use vesafb-tng, +you can either compile the driver into the kernel or use it as a module. +The graphics mode you want to use is in both cases specified using the +standard modedb format. -You should compile in both vgacon (for text mode) and vesafb (for -graphics mode). Which of them takes over the console depends on -whenever the specified mode is text or graphics. +If your system doesn't support vm86 calls, things get a little more tricky. +Since on such systems you can't do BIOS calls from protected mode in which +kernel runs, you have to decide at boot time whenever you want to run in text +or in graphics mode. Switching mode later on is impossible. Switching modes +is done using the vga=... boot parameter. Read Documentation/svga.txt for +details. Below is a more detailed description of what to do on systems using +the standard vesafb driver. -The graphic modes are NOT in the list which you get if you boot with -vga=ask and hit return. The mode you wish to use is derived from the -VESA mode number. Here are those VESA mode numbers: +You should compile in both vgacon (for text mode) and vesafb (for graphics +mode). Which of them takes over the console depends on whenever the +specified mode is text or graphics. + +The graphic modes are NOT in the list which you get if you boot with vga=ask +and hit return. The mode you wish to use is derived from the VESA mode number. +Here are those VESA mode numbers: | 640x480 800x600 1024x768 1280x1024 ----+------------------------------------- -256 | 0x101 0x103 0x105 0x107 -32k | 0x110 0x113 0x116 0x119 -64k | 0x111 0x114 0x117 0x11A -16M | 0x112 0x115 0x118 0x11B +256 | 0x101 0x103 0x105 0x107 +32k | 0x110 0x113 0x116 0x119 +64k | 0x111 0x114 0x117 0x11A +16M | 0x112 0x115 0x118 0x11B -The video mode number of the Linux kernel is the VESA mode number plus -0x200. +The video mode number of the Linux kernel is the VESA mode number plus 0x200. Linux_kernel_mode_number = VESA_mode_number + 0x200 @@ -56,15 +67,15 @@ So the table for the Kernel mode numbers are: | 640x480 800x600 1024x768 1280x1024 ----+------------------------------------- -256 | 0x301 0x303 0x305 0x307 -32k | 0x310 0x313 0x316 0x319 -64k | 0x311 0x314 0x317 0x31A -16M | 0x312 0x315 0x318 0x31B +256 | 0x301 0x303 0x305 0x307 +32k | 0x310 0x313 0x316 0x319 +64k | 0x311 0x314 0x317 0x31A +16M | 0x312 0x315 0x318 0x31B -To enable one of those modes you have to specify "vga=ask" in the -lilo.conf file and rerun LILO. Then you can type in the desired -mode at the "vga=ask" prompt. For example if you like to use -1024x768x256 colors you have to say "305" at this prompt. +To enable one of those modes you have to specify "vga=ask" in the lilo.conf +file and rerun LILO. Then you can type in the desired mode at the "vga=ask" +prompt. For example if you like to use 1024x768x256 colors you have to say +"305" at this prompt. If this does not work, this might be because your BIOS does not support linear framebuffers or because it does not support this mode at all. @@ -72,11 +83,12 @@ Even if your board does, it might be the BIOS which does not. VESA BIOS Extensions v2.0 are required, 1.2 is NOT sufficient. You will get a "bad mode number" message if something goes wrong. -1. Note: LILO cannot handle hex, for booting directly with +1. Note: LILO cannot handle hex, for booting directly with "vga=mode-number" you have to transform the numbers to decimal. 2. Note: Some newer versions of LILO appear to work with those hex values, if you set the 0x in front of the numbers. + X11 === @@ -84,98 +96,164 @@ XF68_FBDev should work just fine, but it is non-accelerated. Running another (accelerated) X-Server like XF86_SVGA might or might not work. It depends on X-Server and graphics board. -The X-Server must restore the video mode correctly, else you end up +The X-Server must restore the video mode correctly, or else you end up with a broken console (and vesafb cannot do anything about this). +With vesafb-tng chances are that the console will be restored properly +even if the X server messes up the video mode. Refresh rates ============= -There is no way to change the vesafb video mode and/or timings after -booting linux. If you are not happy with the 60 Hz refresh rate, you -have these options: +With VBE 3.0 compatible BIOSes and vesafb-tng it is possible to change +the refresh rate either at boot time (by specifying the @<rr> part of +the mode name) or later, using the fbset utility. + +If you want to use the default BIOS refresh rate while switching modes +on a running system, set pixclock to 0. - * configure and load the DOS-Tools for your the graphics board (if - available) and boot linux with loadlin. - * use a native driver (matroxfb/atyfb) instead if vesafb. If none +With VBE 2.0 there is no way to change the mode timings after booting +Linux. If you are not happy with the 60 Hz refresh rate, you have +the following options: + + * Configure and load the DOS tools for your the graphics board (if + available) and boot Linux with loadlin. + * Use a native driver (matroxfb/atyfb) instead of vesafb. If none is available, write a new one! - * VBE 3.0 might work too. I have neither a gfx board with VBE 3.0 - support nor the specs, so I have not checked this yet. + * Use a BIOS editor to change the default refresh rate (such an + editor does exist at least for ATI Radeon BIOSes). + * If you're running a non-vm86 and VBE 3.0 compatible system, you can + use a kernel patch (vesafb-rrc) to hard-code some mode timings in + the kernel and use these while setting the video mode at boot time. + +Note that there are some boards (nVidia 59**, 57** and newer models) +claiming that their Video BIOS is VBE 3.0 compliant, while ignoring the +CRTC values provided by software such as vesafb-tng. You'll not be able +to adjust the refresh rate if you're using one of these boards. Configuration ============= -The VESA BIOS provides protected mode interface for changing -some parameters. vesafb can use it for palette changes and -to pan the display. It is turned off by default because it -seems not to work with some BIOS versions, but there are options -to turn it on. - -You can pass options to vesafb using "video=vesafb:option" on -the kernel command line. Multiple options should be separated -by comma, like this: "video=vesafb:ypan,invers" - -Accepted options: - -invers no comment... - -ypan enable display panning using the VESA protected mode - interface. The visible screen is just a window of the - video memory, console scrolling is done by changing the - start of the window. - pro: * scrolling (fullscreen) is fast, because there is - no need to copy around data. - * You'll get scrollback (the Shift-PgUp thing), - the video memory can be used as scrollback buffer - kontra: * scrolling only parts of the screen causes some - ugly flicker effects (boot logo flickers for - example). - -ywrap Same as ypan, but assumes your gfx board can wrap-around - the video memory (i.e. starts reading from top if it - reaches the end of video memory). Faster than ypan. - -redraw scroll by redrawing the affected part of the screen, this - is the safe (and slow) default. - - -vgapal Use the standard vga registers for palette changes. - This is the default. -pmipal Use the protected mode interface for palette changes. - -mtrr:n setup memory type range registers for the vesafb framebuffer - where n: - 0 - disabled (equivalent to nomtrr) (default) - 1 - uncachable - 2 - write-back - 3 - write-combining - 4 - write-through - - If you see the following in dmesg, choose the type that matches the - old one. In this example, use "mtrr:2". +The VESA BIOS provides protected mode interface for changing some parameters. +vesafb can use it for palette changes and to pan the display. It is turned +off by default because it seems not to work with some BIOS versions, but +there are options to turn it on. + +You can pass options to vesafb using "video=vesafb:option" on the kernel +command line. Multiple options should be separated by a comma, like this: +"video=vesafb:ypan,1024x768-32@85" + +Note that vesafb-tng still uses the "video=vesafb:option" format of the +kernel command line video parameter. "video=vesafb-tng:xxx" is incorrect. + +Accepted options (both vesafb and vesafb-tng): + +ypan Enable display panning using the VESA protected mode interface + The visible screen is just a window of the video memory, + console scrolling is done by changing the start of the window. + pro: * scrolling (fullscreen) is fast, because there is + no need to copy around data. + * you'll get scrollback (the Shift-PgUp thing), + the video memory can be used as scrollback buffer + con: * scrolling only parts of the screen causes some + ugly flicker effects (boot logo flickers for + example). + +ywrap Same as ypan, but assumes your gfx board can wrap-around the video + memory (i.e. starts reading from top if it reaches the end of + video memory). Faster than ypan. + +redraw Scroll by redrawing the affected part of the screen, this is the + safe (and slow) default. + +vgapal Use the standard VGA registers for palette changes. + +pmipal Use the protected mode interface for palette changes. + This is the default is the protected mode interface is available. + +mtrr:n Setup memory type range registers for the vesafb framebuffer + where n: + 0 - disabled (equivalent to nomtrr) (default) + 1 - uncachable + 2 - write-back + 3 - write-combining + 4 - write-through + + If you see the following in dmesg, choose the type that matches + the old one. In this example, use "mtrr:2". ... mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining ... -nomtrr disable mtrr +nomtrr Do not use memory type range registers for vesafb. vremap:n remap 'n' MiB of video RAM. If 0 or not specified, remap memory - according to video mode. (2.5.66 patch/idea by Antonino Daplas - reversed to give override possibility (allocate more fb memory - than the kernel would) to 2.4 by tmb@iki.fi) + according to video mode. (2.5.66 patch/idea by Antonino Daplas + reversed to give override possibility (allocate more fb memory + than the kernel would) to 2.4 by tmb@iki.fi) vtotal:n if the video BIOS of your card incorrectly determines the total amount of video RAM, use this option to override the BIOS (in MiB). -Have fun! +Options accepted only by vesafb-tng: + +<mode> The mode you want to set, in the standard modedb format. Refer to + modedb.txt for a detailed description. If you specify a mode that is + not supported by your board's BIOS, vesafb-tng will attempt to set a + similar mode. The list of supported modes can be found in + /proc/fbx/modes, where x is the framebuffer number (usually 0). + When vesafb-tng is compiled as a module, the mode string should be + provided as a value of the parameter 'mode'. + +vbemode:x + Force the use of VBE mode x. The mode will only be set if it's + found in the VBE-provided list of supported modes. + NOTE: The mode number 'x' should be specified in VESA mode number + notation, not the Linux kernel one (eg. 257 instead of 769). + HINT: If you use this option because normal <mode> parameter does + not work for you and you use a X server, you'll probably want to + set the 'nocrtc' option to ensure that the video mode is properly + restored after console <-> X switches. + +nocrtc Do not use CRTC timings while setting the video mode. This option + makes sence only with VBE 3.0 compliant systems. Use it if you have + problems with modes set in the standard way. Note that using this + option means that any refresh rate adjustments will be ignored + and the refresh rate will stay at your BIOS default (60 Hz). + +noedid Do not try to fetch and use EDID-provided modes. + +noblank Disable hardware blanking. + +gtf Force the use of VESA's GTF (Generalized Timing Formula). Specifying + this will cause vesafb to skip its internal modedb and EDID-modedb + and jump straight to the GTF part of the code (normally used only if + everything else failed). This can be useful if you want to get as + much as possible from your graphics board but your BIOS doesn't + support modes with the refresh rates you require. Note that you may + need to specify the maxhf, maxvf and maxclk parameters if they are not + provided by the EDID block. + +Additionally, the following parameters may be provided. They all override the +EDID-provided values and BIOS defaults. Refer to your monitor's specs to get +the correct values for maxhf, maxvf and maxclk for your hardware. + +maxhf:n Maximum horizontal frequency (in kHz). +maxvf:n Maximum vertical frequency (in Hz). +maxclk:n Maximum pixel clock (in MHz). - Gerd +Have fun! -- +Original document for the vesafb driver by Gerd Knorr <kraxel@goldbach.in-berlin.de> -Minor (mostly typo) changes -by Nico Schmoigl <schmoigl@rumms.uni-mannheim.de> +Minor (mostly typo) changes by +Nico Schmoigl <schmoigl@rumms.uni-mannheim.de> + +Extended documentation for vm86, VBE 3.0 and vesafb-tng by +Michal Januszewski <spock@gentoo.org> + diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S index 2c5b5cc..2c2d4b5 100644 --- a/arch/i386/boot/video.S +++ b/arch/i386/boot/video.S @@ -163,10 +163,12 @@ basret: ret # parameters in the default 80x25 mode -- these are set directly, # because some very obscure BIOSes supply insane values. mode_params: +#ifdef CONFIG_FB_VESA_STD #ifdef CONFIG_VIDEO_SELECT cmpb $0, graphic_mode jnz mopar_gr #endif +#endif movb $0x03, %ah # Read cursor position xorb %bh, %bh int $0x10 @@ -199,6 +201,7 @@ mopar2: movb %al, %fs:(PARAM_VIDEO_LINES) ret #ifdef CONFIG_VIDEO_SELECT +#ifdef CONFIG_FB_VESA_STD # Fetching of VESA frame buffer parameters mopar_gr: leaw modelist+1024, %di @@ -281,6 +284,7 @@ dac_done: movw %es, %fs:(PARAM_VESAPM_SEG) movw %di, %fs:(PARAM_VESAPM_OFF) no_pm: ret +#endif # The video mode menu mode_menu: @@ -495,10 +499,12 @@ mode_set: cmpb $VIDEO_FIRST_V7>>8, %ah jz setv7 - + +#ifdef CONFIG_FB_VESA_STD cmpb $VIDEO_FIRST_VESA>>8, %ah jnc check_vesa - +#endif + orb %ah, %ah jz setmenu @@ -570,6 +576,7 @@ setr1: lodsw movw -4(%si), %ax # Fetch mode ID jmp _m_s +#ifdef CONFIG_FB_VESA_STD check_vesa: leaw modelist+1024, %di subb $VIDEO_FIRST_VESA>>8, %bh @@ -603,6 +610,7 @@ check_vesa: ret _setbad: jmp setbad # Ugly... +#endif # Recalculate vertical display end registers -- this fixes various # inconsistencies of extended modes on many adapters. Called when diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 1393523..8a05f95 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -240,7 +240,7 @@ static void send_sig_all(int sig) struct task_struct *p; for_each_process(p) { - if (p->mm && !is_init(p)) + if (p->mm && !is_init(p) && !(p->flags & PF_BORROWED_MM)) /* Not swapper, init nor kernel thread */ force_sig(sig, p); } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4e83f01..ae122fd 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -547,8 +547,22 @@ config FB_TGA cards. Say Y if you have one of those. config FB_VESA - bool "VESA VGA graphics support" - depends on (FB = y) && X86 + tristate "VESA VGA graphics support" + depends on (FB = y) && (X86 || X86_64) + help + This is the frame buffer device driver for generic VESA 2.0 + compliant graphic cards. The older VESA 1.2 cards are not supported. + You will get a boot time penguin logo at no additional cost. Please + read <file:Documentation/fb/vesafb.txt>. If unsure, say Y. + +choice + prompt "VESA driver type" + depends on FB_VESA + default FB_VESA_STD if X86_64 + default FB_VESA_TNG if X86 + +config FB_VESA_STD + bool "vesafb" select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -557,7 +571,43 @@ config FB_VESA This is the frame buffer device driver for generic VESA 2.0 compliant graphic cards. The older VESA 1.2 cards are not supported. You will get a boot time penguin logo at no additional cost. Please - read <file:Documentation/fb/vesafb.txt>. If unsure, say Y. + read <file:Documentation/fb/vesafb.txt>. Choose this driver if you + are experiencing problems with vesafb-tng or if you own a 64-bit system. + + Note that this driver cannot be compiled as a module. + +config FB_VESA_TNG + bool "vesafb-tng" + depends on !X86_64 + select FB_MODE_HELPERS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is an enhanced generic frame buffer device driver for + VBE 2.0 compliant graphic cards. It can take advantage of VBE 3.0 + features (refresh rate adjustment) when these are available. + The driver also makes it possible to change the video mode + on the fly and to switch back to text mode when it's unloaded. + + If the driver is compiled as a module, the module will be called + vesafb-tng. + +endchoice + +config FB_VESA_DEFAULT_MODE + string "VESA default mode" + depends on FB_VESA_TNG + default "640x480@60" + help + This option is used to determine the default mode vesafb is + supposed to switch to in case no mode is provided as a kernel + command line parameter. + +config VIDEO_SELECT + bool + depends on FB_VESA + default y config FB_IMAC bool "Intel-based Macintosh Framebuffer Support" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 309a26d..e57b0e7 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -102,7 +102,11 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o # Platform or fallback drivers go here -obj-$(CONFIG_FB_VESA) += vesafb.o +ifeq ($(CONFIG_FB_VESA_STD),y) + obj-y += vesafb.o +else + obj-$(CONFIG_FB_VESA) += vesafb-thread.o vesafb-tng.o +endif obj-$(CONFIG_FB_IMAC) += imacfb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o obj-$(CONFIG_FB_OF) += offb.o diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 3cfea31..bfb39cc 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1408,6 +1408,7 @@ fbmem_init(void) printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class)); fb_class = NULL; } + return 0; } diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 5df41f6..f838a53 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -674,6 +674,7 @@ void fb_var_to_videomode(struct fb_videomode *mode, { u32 pixclock, hfreq, htotal, vtotal; + mode->refresh = 0; mode->name = NULL; mode->xres = var->xres; mode->yres = var->yres; @@ -1025,3 +1026,4 @@ EXPORT_SYMBOL(fb_find_best_mode); EXPORT_SYMBOL(fb_find_nearest_mode); EXPORT_SYMBOL(fb_videomode_to_modelist); EXPORT_SYMBOL(fb_find_mode); +EXPORT_SYMBOL(fb_destroy_modelist); diff --git a/drivers/video/vesafb-thread.c b/drivers/video/vesafb-thread.c new file mode 100644 index 0000000..543e202 --- /dev/null +++ b/drivers/video/vesafb-thread.c @@ -0,0 +1,751 @@ +/* + * Framebuffer driver for VBE 2.0+ compliant graphic boards. + * Kernel thread and vm86 routines. + * + * (c) 2004-2006 Michal Januszewski <spock@gentoo.org> + * + */ + +#include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/signal.h> +#include <linux/freezer.h> +#include <linux/suspend.h> +#include <linux/unistd.h> +#include <video/vesa.h> +#include <video/edid.h> +#include <asm/mman.h> +#include <asm/page.h> +#include <asm/vm86.h> +#include <asm/thread_info.h> +#include <asm/uaccess.h> +#include <asm/mmu_context.h> +#include "edid.h" + +static int errno; + +static DECLARE_COMPLETION(vesafb_th_completion); +static DECLARE_MUTEX(vesafb_task_list_sem); +static LIST_HEAD(vesafb_task_list); +static DECLARE_WAIT_QUEUE_HEAD(vesafb_wait); + +static struct vm86_struct vm86; +static int vesafb_pid = 0; + +#define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK) +#define VM86_PUSHW(x) \ +do { \ + vm86.regs.esp -= 2; \ + *(u16*)(STACK_ADDR + vm86.regs.esp) = x; \ +} while(0); + +/* Stack, the return code and buffers will be put into + * one contiguous memory chunk: + * + * [ STACK | RET_CODE | BUFFER ] + * + * Some video BIOSes (sis6326) try to store data somewhere + * in 0x7000-0x7fff, so we zeromap more memory to be safe. + */ +#define IVTBDA_SIZE PAGE_SIZE +#define RET_CODE_SIZE 0x0010 +#define STACK_SIZE 0x0500 +#define BUFFER_SIZE 0x10000 + +/* The amount of memory that will be allocated should be a multiple + * of PAGE_SIZE. */ +#define __MEM_SIZE (RET_CODE_SIZE + STACK_SIZE + BUFFER_SIZE) +#define REAL_MEM_SIZE (((__MEM_SIZE / PAGE_SIZE) + 1) * PAGE_SIZE) + +#define IVTBDA_ADDR 0x00000 +#define STACK_ADDR (IVTBDA_ADDR + IVTBDA_SIZE) +#define RET_CODE_ADDR (STACK_ADDR + STACK_SIZE) +#define BUF_ADDR (RET_CODE_ADDR + RET_CODE_SIZE) + +#define FLAG_D (1 << 10) + +/* Syscalls used by the vesafb thread */ +static int vm86old(struct vm86_struct __user* v86) +{ + long res; + __asm__ volatile ("push %%ebx; movl %2, %%ebx ; int $0x80 ; pop %%ebx" + : "=a" (res) + : "0" (__NR_vm86old), "ri" ((long)(v86)) : "memory"); + + if ((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)) { + errno = -res; + res = -1; + } + return (int)res; +} + +static int ioperm(unsigned long a, unsigned long b, unsigned long c) +{ + long res; + __asm__ volatile ("push %%ebx; movl %2, %%ebx ; int $0x80 ; pop %%ebx" + : "=a" (res) + : "0" (__NR_ioperm), "ri" ((long)(a)), "c" ((long)(b)), + "d" ((long)(c)) : "memory"); + + if ((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)) { + errno = -res; + res = -1; + } + return (int)res; +} + +/* Segment prefix opcodes */ +enum { + P_CS = 0x2e, + P_SS = 0x36, + P_DS = 0x3e, + P_ES = 0x26, + P_FS = 0x64, + P_GS = 0x65 +}; + +/* Emulated vm86 ins instruction */ +static void vm86_ins(int size) +{ + u32 edx, edi; + edx = vm86.regs.edx & 0xffff; + edi = (vm86.regs.edi & 0xffff) + (u32)(vm86.regs.es << 4); + + if (vm86.regs.eflags & FLAG_D) + asm volatile ("std\n"); + else + asm volatile ("cld\n"); + + switch (size) { + case 4: + asm volatile ("insl\n" : "=D" (edi) : "d" (edx), "0" (edi)); + break; + case 2: + asm volatile ("insw\n" : "=D" (edi) : "d" (edx), "0" (edi)); + break; + case 1: + asm volatile ("insb\n" : "=D" (edi) : "d" (edx), "0" (edi)); + break; + } + + if (vm86.regs.eflags & FLAG_D) + asm volatile ("cld\n"); + + edi -= (u32)(vm86.regs.es << 4); + + vm86.regs.edi &= 0xffff0000; + vm86.regs.edi |= edi & 0xffff; +} + +static void vm86_rep_ins(int size) +{ + u16 cx = vm86.regs.ecx; + while (cx--) + vm86_ins(size); + + vm86.regs.ecx &= 0xffff0000; +} + +/* Emulated vm86 outs instruction */ +static void vm86_outs(int size, int segment) +{ + u32 edx, esi, base; + + edx = vm86.regs.edx & 0xffff; + esi = vm86.regs.esi & 0xffff; + + switch (segment) { + case P_CS: base = vm86.regs.cs; break; + case P_SS: base = vm86.regs.ss; break; + case P_ES: base = vm86.regs.es; break; + case P_FS: base = vm86.regs.fs; break; + case P_GS: base = vm86.regs.gs; break; + default: base = vm86.regs.ds; break; + } + + esi += base << 4; + + if (vm86.regs.eflags & FLAG_D) + asm volatile ("std\n"); + else + asm volatile ("cld\n"); + + switch (size) { + case 4: + asm volatile ("outsl\n" : "=S" (esi) : "d" (edx), "0" (esi)); + break; + case 2: + asm volatile ("outsw\n" : "=S" (esi) : "d" (edx), "0" (esi)); + break; + case 1: + asm volatile ("outsb\n" : "=S" (esi) : "d" (edx), "0" (esi)); + break; + } + + if (vm86.regs.eflags & FLAG_D) + asm volatile ("cld"); + + esi -= base << 4; + vm86.regs.esi &= 0xffff0000; + vm86.regs.esi |= (esi & 0xffff); +} + +static void vm86_rep_outs(int size, int segment) +{ + u16 cx = vm86.regs.ecx; + while (cx--) + vm86_outs(size, segment); + + vm86.regs.ecx &= 0xffff0000; +} + +static int vm86_do_unknown(void) +{ + u8 data32 = 0, segment = P_DS, rep = 0; + u8 *instr; + int ret = 0, i = 0; + + instr = (u8*)((vm86.regs.cs << 4) + vm86.regs.eip); + + while (1) { + switch(instr[i]) { + case 0x66: /* operand size prefix */ + data32 = 1 - data32; + i++; + break; + case 0xf2: /* repnz */ + case 0xf3: /* rep */ + rep = 1; + i++; + break; + case P_CS: /* segment prefix */ + case P_SS: + case P_DS: + case P_ES: + case P_FS: + case P_GS: + segment = instr[i]; + i++; + break; + case 0xf0: /* LOCK - ignored */ + case 0x67: /* address size prefix - ignored */ + i++; + break; + case 0x6c: /* insb */ + if (rep) + vm86_rep_ins(1); + else + vm86_ins(1); + i++; + goto out; + case 0x6d: /* insw / insd */ + if (rep) { + if (data32) + vm86_rep_ins(4); + else + vm86_rep_ins(2); + } else { + if (data32) + vm86_ins(4); + else + vm86_ins(2); + } + i++; + goto out; + case 0x6e: /* outsb */ + if (rep) + vm86_rep_outs(1, segment); + else + vm86_outs(1, segment); + i++; + goto out; + case 0x6f: /* outsw / outsd */ + if (rep) { + if (data32) + vm86_rep_outs(4, segment); + else + vm86_rep_outs(2, segment); + } else { + if (data32) + vm86_outs(4, segment); + else + vm86_outs(2, segment); + } + i++; + goto out; + case 0xe4: /* inb xx */ + asm volatile ( + "inb %w1, %b0" + : "=a" (vm86.regs.eax) + : "d" (instr[i+1]), "0" (vm86.regs.eax)); + i += 2; + goto out; + case 0xe5: /* inw xx / ind xx */ + if (data32) { + asm volatile ( + "inl %w1, %0" + : "=a" (vm86.regs.eax) + : "d" (instr[i+1]), + "0" (vm86.regs.eax)); + } else { + asm volatile ( + "inw %w1, %w0" + : "=a" (vm86.regs.eax) + : "d" (instr[i+1]), + "0" (vm86.regs.eax)); + } + i += 2; + goto out; + + case 0xec: /* inb dx */ + asm volatile ( + "inb %w1, %b0" + : "=a" (vm86.regs.eax) + : "d" (vm86.regs.edx), "0" (vm86.regs.eax)); + i++; + goto out; + case 0xed: /* inw dx / ind dx */ + if (data32) { + asm volatile ( + "inl %w1, %0" + : "=a" (vm86.regs.eax) + : "d" (vm86.regs.edx)); + } else { + asm volatile ( + "inw %w1, %w0" + : "=a" (vm86.regs.eax) + : "d" (vm86.regs.edx)); + } + i++; + goto out; + case 0xe6: /* outb xx */ + asm volatile ( + "outb %b0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), "d" (instr[i+1])); + i += 2; + goto out; + case 0xe7: /* outw xx / outd xx */ + if (data32) { + asm volatile ( + "outl %0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (instr[i+1])); + } else { + asm volatile ( + "outw %w0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (instr[i+1])); + } + i += 2; + goto out; + case 0xee: /* outb dx */ + asm volatile ( + "outb %b0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), "d" (vm86.regs.edx)); + i++; + goto out; + case 0xef: /* outw dx / outd dx */ + if (data32) { + asm volatile ( + "outl %0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (vm86.regs.edx)); + } else { + asm volatile ( + "outw %w0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (vm86.regs.edx)); + } + i++; + goto out; + default: + printk(KERN_ERR "vesafb: BUG, opcode 0x%x emulation " + "not supported (EIP: 0x%lx)\n", + instr[i], (u32)(vm86.regs.cs << 4) + + vm86.regs.eip); + ret = 1; + goto out; + } + } +out: vm86.regs.eip += i; + return ret; +} + +void vesafb_do_vm86(struct vm86_regs *regs) +{ + unsigned int ret; + u8 *retcode = (void*)RET_CODE_ADDR; + + memset(&vm86,0,sizeof(vm86)); + memcpy(&vm86.regs, regs, sizeof(struct vm86_regs)); + + /* The return code */ + retcode[0] = 0xcd; /* int opcode */ + retcode[1] = 0xff; /* int number (255) */ + + /* We use int 0xff to get back to protected mode */ + memset(&vm86.int_revectored, 0, sizeof(vm86.int_revectored)); + ((unsigned char *)&vm86.int_revectored)[0xff / 8] |= (1 << (0xff % 8)); + + /* + * We want to call int 0x10, so we set: + * CS = 0x42 = 0x10 * 4 + 2 + * IP = 0x40 = 0x10 * 4 + * and SS:ESP. It's up to the caller to set the rest of the registers. + */ + vm86.regs.eflags = DEFAULT_VM86_FLAGS; + vm86.regs.cs = *(unsigned short *)0x42; + vm86.regs.eip = *(unsigned short *)0x40; + vm86.regs.ss = (STACK_ADDR >> 4); + vm86.regs.esp = ((STACK_ADDR & 0x0000f) + STACK_SIZE); + + /* These will be fetched off the stack when we come to an iret in the + * int's 0x10 code. */ + VM86_PUSHW(DEFAULT_VM86_FLAGS); + VM86_PUSHW((RET_CODE_ADDR >> 4)); /* return code segment */ + VM86_PUSHW((RET_CODE_ADDR & 0x0000f)); /* return code offset */ + + while(1) { + ret = vm86old(&vm86); + + if (VM86_TYPE(ret) == VM86_INTx) { + int vint = VM86_ARG(ret); + + /* If exit from vm86 was caused by int 0xff, then + * we're done.. */ + if (vint == 0xff) + goto out; + + /* .. otherwise, we have to call the int handler + * manually */ + VM86_PUSHW(vm86.regs.eflags); + VM86_PUSHW(vm86.regs.cs); + VM86_PUSHW(vm86.regs.eip); + + vm86.regs.cs = *(u16 *)((vint << 2) + 2); + vm86.regs.eip = *(u16 *)(vint << 2); + vm86.regs.eflags &= ~(VIF_MASK | TF_MASK); + } else if (VM86_TYPE(ret) == VM86_UNKNOWN) { + if (vm86_do_unknown()) + goto out; + } else { + printk(KERN_ERR "vesafb: BUG, returned from " + "vm86 with %x (EIP: 0x%lx)\n", + ret, (u32)(vm86.regs.cs << 4) + + vm86.regs.eip); + goto out; + } + } + +out: /* copy the registers' state back to the caller's struct */ + memcpy(regs, &vm86.regs, sizeof(struct vm86_regs)); +} + +static int vesafb_remap_pfn_range(unsigned long start, unsigned long end, + unsigned long pgoff, unsigned long prot, + int type) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + int ret = 0; + + vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL); + if (!vma) + return -ENOMEM; + memset(vma, 0, sizeof(*vma)); + down_write(&mm->mmap_sem); + vma->vm_mm = mm; + vma->vm_start = start; + vma->vm_end = end; + vma->vm_flags = VM_READ | VM_WRITE | VM_EXEC; + vma->vm_flags |= mm->def_flags; + vma->vm_page_prot.pgprot = prot; + vma->vm_pgoff = pgoff; + + if ((ret = insert_vm_struct(mm, vma))) { + up_write(&mm->mmap_sem); + kmem_cache_free(vm_area_cachep, vma); + return ret; + } + + if (type) { + ret = zeromap_page_range(vma, + vma->vm_start, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + } else { + vma->vm_flags |= VM_SHARED; + ret = remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + } + up_write(&mm->mmap_sem); + return ret; +} + +static inline int vesafb_init_mem(void) +{ + int ret = 0; + + /* The memory chunks we're remapping here should be multiples + * of PAGE_SIZE. */ + ret += vesafb_remap_pfn_range(0x00000, IVTBDA_SIZE, 0, + PROT_READ | PROT_EXEC | PROT_WRITE, 0); + ret += vesafb_remap_pfn_range(IVTBDA_SIZE, REAL_MEM_SIZE, 0, + PROT_READ | PROT_EXEC | PROT_WRITE, 1); + ret += vesafb_remap_pfn_range(0x9f000, 0x100000, + 0x9f000 >> PAGE_SHIFT, + PROT_READ | PROT_EXEC | PROT_WRITE, 0); + if (ret) + printk(KERN_ERR "vesafb thread: memory remapping failed\n"); + + return ret; +} + +#define vesafb_get_string(str) \ +{ \ + /* The address is in the form ssssoooo, where oooo = offset, \ + * ssss = segment */ \ + addr = ((p_vbe(tsk->buf)->str & 0xffff0000) >> 12) + \ + (p_vbe(tsk->buf)->str & 0x0000ffff); \ + \ + /* The data is in ROM which is shared between processes, so we \ + * just translate the real mode address into one visible from \ + * kernel space */ \ + if (addr >= 0xa0000) { \ + p_vbe(tsk->buf)->str = (u32) __va(addr); \ + \ + /* The data is in the buffer, we just have to convert the \ + * address so that it points into the buffer user provided. */ \ + } else if (addr > BUF_ADDR && addr < BUF_ADDR + \ + sizeof(struct vesafb_vbe_ib)) { \ + addr -= BUF_ADDR; \ + p_vbe(tsk->buf)->str = (u32) (tsk->buf + addr); \ + \ + /* This should never happen: someone was insane enough to put \ + * the data somewhere in RAM.. */ \ + } else { \ + p_vbe(tsk->buf)->str = (u32) ""; \ + } \ +} + +void vesafb_handle_getvbeib(struct vesafb_task *tsk) +{ + int addr, res; + + tsk->regs.es = (BUF_ADDR >> 4); + tsk->regs.edi = (BUF_ADDR & 0x000f); + strncpy(p_vbe(BUF_ADDR)->vbe_signature, "VBE2", 4); + + vesafb_do_vm86(&tsk->regs); + memcpy(tsk->buf, (void*)(BUF_ADDR), sizeof(struct vesafb_vbe_ib)); + + /* The OEM fields were not defined prior to VBE 2.0 */ + if (p_vbe(tsk->buf)->vbe_version >= 0x200) { + vesafb_get_string(oem_string_ptr); + vesafb_get_string(oem_vendor_name_ptr); + vesafb_get_string(oem_product_name_ptr); + vesafb_get_string(oem_product_rev_ptr); + } + + /* This is basically the same as vesafb_get_string() */ + addr = ((p_vbe(tsk->buf)->mode_list_ptr & 0xffff0000) >> 12) + + (p_vbe(tsk->buf)->mode_list_ptr & 0x0000ffff); + + if (addr >= 0xa0000) { + p_vbe(tsk->buf)->mode_list_ptr = (u32) __va(addr); + } else if (addr > BUF_ADDR && addr < BUF_ADDR + + sizeof(struct vesafb_vbe_ib)) { + addr -= BUF_ADDR; + p_vbe(tsk->buf)->mode_list_ptr = (u32) (tsk->buf + addr); + } else { + res = 0; + printk(KERN_WARNING "vesafb: warning, copying modelist " + "from somewhere in RAM!\n"); + while (*(u16*)(addr+res) != 0xffff && + res < (sizeof(p_vbe(tsk->buf)->reserved) - 2)) { + *(u16*) ((u32)&(p_vbe(tsk->buf)->reserved) + res) = + *(u16*)(addr+res); + res += 2; + } + *(u16*) ((u32)&(p_vbe(tsk->buf)->reserved) + res) = 0xffff; + } +} + +int vesafb_handle_tasks(void) +{ + struct vesafb_task *tsk; + struct list_head *curr, *next; + int ret = 0; + + down(&vesafb_task_list_sem); + list_for_each_safe(curr, next, &vesafb_task_list) { + tsk = list_entry(curr, struct vesafb_task, node); + + if (tsk->flags & TF_EXIT) { + ret = 1; + goto task_done; + } + if (tsk->flags & TF_GETVBEIB) { + vesafb_handle_getvbeib(tsk); + goto task_done; + } + /* Do we need to store a pointer to the buffer in ES:EDI? */ + if (tsk->flags & TF_BUF_DI) { + tsk->regs.es = (BUF_ADDR >> 4); + tsk->regs.edi = (BUF_ADDR & 0x000f); + } + /* Sometimes the pointer has to be in ES:EBX. */ + if (tsk->flags & TF_BUF_BX) { + tsk->regs.es = (BUF_ADDR >> 4); + tsk->regs.ebx = (BUF_ADDR & 0x000f); + } + if (tsk->flags & (TF_BUF_DI | TF_BUF_BX)) + memcpy((void*)BUF_ADDR, tsk->buf, tsk->buf_len); + + vesafb_do_vm86(&tsk->regs); + + if (tsk->flags & TF_RETURN_BUF) + memcpy(tsk->buf, (void*)BUF_ADDR, tsk->buf_len); + +task_done: list_del(curr); + complete(&tsk->done); + } + + /* If we're going to kill this thread, don't allow any elements + * to be added to the task list. */ + if (!ret) + up(&vesafb_task_list_sem); + + return ret; +} + +/* + * This 'hybrid' thread serves as a backend for vesafb-tng, handling all vm86 + * calls. It is started as a kernel thread. It then creates its own mm struct, + * thus separating itself from any userspace processes. At this moment, it + * stops being a kernel thread (kernel threads have mm = NULL) and becomes + * a 'hybrid' thread -- one that has full access to kernel space, yet runs + * with its own address space. + * + * This is necessary because in order to make vm86 calls some parts of the + * first 1MB of RAM have to be setup to mimic the real mode. These are: + * - interrupt vector table [0x00000-0x003ff] + * - BIOS data area [0x00400-0x004ff] + * - Extended BIOS data area [0x9fc00-0x9ffff] + * - the video RAM [0xa0000-0xbffff] + * - video BIOS [0xc0000-0xcffff] + * - motherboard BIOS [0xf0000-0xfffff] + */ +int vesafb_thread(void *unused) +{ + int err = 0; + + set_fs(KERNEL_DS); + daemonize("vesafb"); + + if (set_new_mm()) { + err = -ENOMEM; + goto thr_end; + } + if (vesafb_init_mem()) { + err = -ENOMEM; + goto thr_end; + } + + DPRINTK("started vesafb thread\n"); + + /* Having an IO bitmap makes things faster as we avoid GPFs + * when running vm86 code. We can live if it fails, though, + * so don't bother checking for errors. */ + ioperm(0,1024,1); + set_user_nice(current, -10); + + complete(&vesafb_th_completion); + + while (1) { + if (vesafb_handle_tasks()) + break; + wait_event_interruptible(vesafb_wait, + !list_empty(&vesafb_task_list)); + try_to_freeze(); + } + +out: DPRINTK("exiting the vesafb thread\n"); + vesafb_pid = -1; + + /* Now that all callers know this thread is no longer running + * (pid < 0), allow them to continue. */ + up(&vesafb_task_list_sem); + return err; +thr_end: + down(&vesafb_task_list_sem); + complete(&vesafb_th_completion); + goto out; +} + +int vesafb_queue_task(struct vesafb_task *tsk) +{ + down(&vesafb_task_list_sem); + if (vesafb_pid < 0) + return -1; + list_add_tail(&tsk->node, &vesafb_task_list); + up(&vesafb_task_list_sem); + wake_up(&vesafb_wait); + return 0; +} + +int vesafb_wait_for_thread(void) +{ + /* PID 0 means that the thread is still initializing. */ + if (vesafb_pid < 0) + return -1; + wait_for_completion(&vesafb_th_completion); + return 0; +} + +int __init vesafb_init_thread(void) +{ + vesafb_pid = kernel_thread(vesafb_thread,NULL,0); + return 0; +} + +#ifdef MODULE +void __exit vesafb_kill_thread(void) +{ + struct vesafb_task *tsk; + if (vesafb_pid <= 0) + return; + + vesafb_create_task(tsk); + if (!tsk) + return; + tsk->flags |= TF_EXIT; + vesafb_queue_task(tsk); + vesafb_wait_for_task(tsk); + kfree(tsk); + return; +} +module_exit(vesafb_kill_thread); +#endif +module_init(vesafb_init_thread); + +EXPORT_SYMBOL_GPL(vesafb_queue_task); +EXPORT_SYMBOL_GPL(vesafb_wait_for_thread); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Januszewski"); + diff --git a/drivers/video/vesafb-tng.c b/drivers/video/vesafb-tng.c new file mode 100644 index 0000000..b4d4394 --- /dev/null +++ b/drivers/video/vesafb-tng.c @@ -0,0 +1,1586 @@ +/* + * Framebuffer driver for VBE 2.0+ compliant graphic boards + * + * (c) 2004-2006 Michal Januszewski <spock@gentoo.org> + * Based upon vesafb code by Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/completion.h> +#include <linux/platform_device.h> +#include <video/edid.h> +#include <video/vesa.h> +#include <video/vga.h> +#include <asm/io.h> +#include <asm/mtrr.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include "edid.h" + +#define dac_reg (0x3c8) +#define dac_val (0x3c9) + +#define VESAFB_NEED_EXACT_RES 1 +#define VESAFB_NEED_EXACT_DEPTH 2 + +/* --------------------------------------------------------------------- */ + +static struct fb_var_screeninfo vesafb_defined __initdata = { + .activate = FB_ACTIVATE_NOW, + .height = 0, + .width = 0, + .right_margin = 32, + .upper_margin = 16, + .lower_margin = 4, + .vsync_len = 4, + .vmode = FB_VMODE_NONINTERLACED, +}; + +static struct fb_fix_screeninfo vesafb_fix __initdata = { + .id = "VESA VGA", + .type = FB_TYPE_PACKED_PIXELS, + .accel = FB_ACCEL_NONE, +}; + +static int mtrr = 0; /* disable mtrr by default */ +static int blank = 1; /* enable blanking by default */ +static int ypan = 0; /* 0 - nothing, 1 - ypan, 2 - ywrap */ +static int pmi_setpal = 1; /* pmi for palette changes */ +static u16 *pmi_base = NULL; /* protected mode interface location */ +static void (*pmi_start)(void) = NULL; +static void (*pmi_pal)(void) = NULL; +static struct vesafb_vbe_ib vbe_ib; +static struct vesafb_mode_ib *vbe_modes; +static int vbe_modes_cnt = 0; +static struct fb_info *vesafb_info = NULL; +static int nocrtc = 0; /* ignore CRTC settings */ +static int noedid __initdata = 0; /* don't try DDC transfers */ +static int vram_remap __initdata = 0; /* set amount of memory to be used */ +static int vram_total __initdata = 0; /* set total amount of memory */ +static u16 maxclk __initdata = 0; /* maximum pixel clock */ +static u16 maxvf __initdata = 0; /* maximum vertical frequency */ +static u16 maxhf __initdata = 0; /* maximum horizontal frequency */ +static int gtf __initdata = 0; /* forces use of the GTF */ +static char *mode_option __initdata = NULL; +static u16 vbemode __initdata = 0; + +/* --------------------------------------------------------------------- */ + +static int vesafb_find_vbe_mode(int xres, int yres, int depth, + unsigned char flags) +{ + int i, match = -1, h = 0, d = 0x7fffffff; + + for (i = 0; i < vbe_modes_cnt; i++) { + h = abs(vbe_modes[i].x_res - xres) + + abs(vbe_modes[i].y_res - yres) + + abs(depth - vbe_modes[i].depth); + if (h == 0) + return i; + if (h < d || (h == d && vbe_modes[i].depth > depth)) { + d = h; + match = i; + } + } + i = 1; + + if (flags & VESAFB_NEED_EXACT_DEPTH && vbe_modes[match].depth != depth) + i = 0; + if (flags & VESAFB_NEED_EXACT_RES && d > 24) + i = 0; + if (i != 0) + return match; + else + return -1; +} + +static int vesafb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int offset; + + offset = (var->yoffset * info->fix.line_length + var->xoffset) / 4; + + /* It turns out it's not the best idea to do panning via vm86, + * so we only allow it if we have a PMI. */ + if (pmi_start) { + __asm__ __volatile__( + "call *(%%edi)" + : /* no return value */ + : "a" (0x4f07), /* EAX */ + "b" (0), /* EBX */ + "c" (offset), /* ECX */ + "d" (offset >> 16), /* EDX */ + "D" (&pmi_start)); /* EDI */ + } + return 0; +} + +static int vesafb_blank(int blank, struct fb_info *info) +{ + struct vesafb_task *tsk; + int err = 1; + + if (vbe_ib.capabilities & VBE_CAP_VGACOMPAT) { + int loop = 10000; + u8 seq = 0, crtc17 = 0; + + if (blank == FB_BLANK_POWERDOWN) { + seq = 0x20; + crtc17 = 0x00; + err = 0; + } else { + seq = 0x00; + crtc17 = 0x80; + err = (blank == FB_BLANK_UNBLANK) ? 0 : -EINVAL; + } + + vga_wseq(NULL, 0x00, 0x01); + seq |= vga_rseq(NULL, 0x01) & ~0x20; + vga_wseq(NULL, 0x00, seq); + + crtc17 |= vga_rcrt(NULL, 0x17) & ~0x80; + while (loop--); + vga_wcrt(NULL, 0x17, crtc17); + vga_wseq(NULL, 0x00, 0x03); + } else { + vesafb_create_task (tsk); + if (!tsk) + return -ENOMEM; + tsk->regs.eax = 0x4f10; + switch (blank) { + case FB_BLANK_UNBLANK: + tsk->regs.ebx = 0x0001; + break; + case FB_BLANK_NORMAL: + tsk->regs.ebx = 0x0101; /* standby */ + break; + case FB_BLANK_POWERDOWN: + tsk->regs.ebx = 0x0401; /* powerdown */ + break; + default: + goto out; + } + tsk->flags = TF_CALL; + if (!vesafb_queue_task (tsk)) + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) == 0x004f) + err = 0; +out: kfree(tsk); + } + return err; +} + +static int vesafb_setpalette(struct vesafb_pal_entry *entries, int count, + int start, struct fb_info *info) +{ + struct vesafb_task *tsk; + int i = ((struct vesafb_par*)info->par)->mode_idx; + int ret = 0; + + /* We support palette modifications for 8 bpp modes only, so + * there can never be more than 256 entries. */ + if (start + count > 256) + return -EINVAL; + + /* Use VGA registers if mode is VGA-compatible. */ + if (i >= 0 && i < vbe_modes_cnt && + vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) { + for (i = 0; i < count; i++) { + outb_p(start + i, dac_reg); + outb_p(entries[i].red, dac_val); + outb_p(entries[i].green, dac_val); + outb_p(entries[i].blue, dac_val); + } + } else if (pmi_setpal) { + __asm__ __volatile__( + "call *(%%esi)" + : /* no return value */ + : "a" (0x4f09), /* EAX */ + "b" (0), /* EBX */ + "c" (count), /* ECX */ + "d" (start), /* EDX */ + "D" (entries), /* EDI */ + "S" (&pmi_pal)); /* ESI */ + } else { + vesafb_create_task (tsk); + if (!tsk) + return -ENOMEM; + tsk->regs.eax = 0x4f09; + tsk->regs.ebx = 0x0; + tsk->regs.ecx = count; + tsk->regs.edx = start; + tsk->buf = entries; + tsk->buf_len = sizeof(struct vesafb_pal_entry) * count; + tsk->flags = TF_CALL | TF_BUF_DI; + + if (!vesafb_queue_task (tsk)) + vesafb_wait_for_task(tsk); + if ((tsk->regs.eax & 0xffff) != 0x004f) + ret = 1; + kfree(tsk); + } + return ret; +} + +static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct vesafb_pal_entry entry; + int shift = 16 - info->var.green.length; + int ret = 0; + + if (regno >= info->cmap.len) + return -EINVAL; + + if (info->var.bits_per_pixel == 8) { + entry.red = red >> shift; + entry.green = green >> shift; + entry.blue = blue >> shift; + entry.pad = 0; + + ret = vesafb_setpalette(&entry, 1, regno, info); + } else if (regno < 16) { + switch (info->var.bits_per_pixel) { + case 16: + if (info->var.red.offset == 10) { + /* 1:5:5:5 */ + ((u32*) (info->pseudo_palette))[regno] = + ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + } else { + /* 0:5:6:5 */ + ((u32*) (info->pseudo_palette))[regno] = + ((red & 0xf800) ) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + } + break; + + case 24: + case 32: + red >>= 8; + green >>= 8; + blue >>= 8; + ((u32 *)(info->pseudo_palette))[regno] = + (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + break; + } + } + return ret; +} + +static int vesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + struct vesafb_pal_entry *entries; + int shift = 16 - info->var.green.length; + int i, ret = 0; + + if (info->var.bits_per_pixel == 8) { + if (cmap->start + cmap->len > info->cmap.start + + info->cmap.len || cmap->start < info->cmap.start) + return -EINVAL; + + entries = vmalloc(sizeof(struct vesafb_pal_entry) * cmap->len); + if (!entries) + return -ENOMEM; + for (i = 0; i < cmap->len; i++) { + entries[i].red = cmap->red[i] >> shift; + entries[i].green = cmap->green[i] >> shift; + entries[i].blue = cmap->blue[i] >> shift; + entries[i].pad = 0; + } + ret = vesafb_setpalette(entries, cmap->len, cmap->start, info); + vfree(entries); + } else { + /* For modes with bpp > 8, we only set the pseudo palette in + * the fb_info struct. We rely on vesafb_setcolreg to do all + * sanity checking. */ + for (i = 0; i < cmap->len; i++) { + ret += vesafb_setcolreg(cmap->start + i, cmap->red[i], + cmap->green[i], cmap->blue[i], + 0, info); + } + } + return ret; +} + +static int vesafb_set_par(struct fb_info *info) +{ + struct vesafb_par *par = (struct vesafb_par *) info->par; + struct vesafb_task *tsk; + struct vesafb_crtc_ib *crtc = NULL; + struct vesafb_mode_ib *mode = NULL; + int i, err = 0, depth = info->var.bits_per_pixel; + + if (depth > 8 && depth != 32) + depth = info->var.red.length + info->var.green.length + + info->var.blue.length; + + i = vesafb_find_vbe_mode(info->var.xres, info->var.yres, depth, + VESAFB_NEED_EXACT_RES | + VESAFB_NEED_EXACT_DEPTH); + if (i >= 0) + mode = &vbe_modes[i]; + else + return -EINVAL; + + vesafb_create_task (tsk); + if (!tsk) + return -ENOMEM; + tsk->regs.eax = 0x4f02; + tsk->regs.ebx = mode->mode_id | 0x4000; /* use LFB */ + tsk->flags = TF_CALL; + + if (vbe_ib.vbe_version >= 0x0300 && !nocrtc && + info->var.pixclock != 0) { + tsk->regs.ebx |= 0x0800; /* use CRTC data */ + tsk->flags |= TF_BUF_DI; + crtc = kmalloc(sizeof(struct vesafb_crtc_ib), GFP_KERNEL); + if (!crtc) { + err = -ENOMEM; + goto out; + } + crtc->horiz_start = info->var.xres + info->var.right_margin; + crtc->horiz_end = crtc->horiz_start + info->var.hsync_len; + crtc->horiz_total = crtc->horiz_end + info->var.left_margin; + + crtc->vert_start = info->var.yres + info->var.lower_margin; + crtc->vert_end = crtc->vert_start + info->var.vsync_len; + crtc->vert_total = crtc->vert_end + info->var.upper_margin; + + crtc->pixel_clock = PICOS2KHZ(info->var.pixclock) * 1000; + crtc->refresh_rate = (u16)(100 * (crtc->pixel_clock / + (crtc->vert_total * crtc->horiz_total))); + crtc->flags = 0; + + if (info->var.vmode & FB_VMODE_DOUBLE) + crtc->flags |= 0x1; + if (info->var.vmode & FB_VMODE_INTERLACED) + crtc->flags |= 0x2; + if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + crtc->flags |= 0x4; + if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + crtc->flags |= 0x8; + memcpy(&par->crtc, crtc, sizeof(struct vesafb_crtc_ib)); + } else + memset(&par->crtc, 0, sizeof(struct vesafb_crtc_ib)); + + tsk->buf = (void*)crtc; + tsk->buf_len = sizeof(struct vesafb_crtc_ib); + + if (vesafb_queue_task (tsk)) { + err = -EINVAL; + goto out; + } + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f) { + printk(KERN_ERR "vesafb: mode switch failed (eax: 0x%lx)\n", + tsk->regs.eax); + err = -EINVAL; + goto out; + } + par->mode_idx = i; + + /* For 8bpp modes, always try to set the DAC to 8 bits. */ + if (vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC && + mode->bits_per_pixel <= 8) { + vesafb_reset_task(tsk); + tsk->flags = TF_CALL; + tsk->regs.eax = 0x4f08; + tsk->regs.ebx = 0x0800; + + if (!vesafb_queue_task (tsk)) + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f || + ((tsk->regs.ebx & 0xff00) >> 8) != 8) { + /* We've failed to set the DAC palette format - + * time to correct var. */ + info->var.red.length = 6; + info->var.green.length = 6; + info->var.blue.length = 6; + } + } + + info->fix.visual = (info->var.bits_per_pixel == 8) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = mode->bytes_per_scan_line; + + DPRINTK("set new mode %dx%d-%d (0x%x)\n", + info->var.xres, info->var.yres, info->var.bits_per_pixel, + mode->mode_id); + +out: if (crtc != NULL) + kfree(crtc); + kfree(tsk); + + return err; +} + +static void vesafb_setup_var(struct fb_var_screeninfo *var, struct fb_info *info, + struct vesafb_mode_ib *mode) +{ + var->xres = mode->x_res; + var->yres = mode->y_res; + var->xres_virtual = mode->x_res; + var->yres_virtual = (ypan) ? + info->fix.smem_len / mode->bytes_per_scan_line : + mode->y_res; + var->xoffset = 0; + var->yoffset = 0; + var->bits_per_pixel = mode->bits_per_pixel; + + if (var->bits_per_pixel == 15) + var->bits_per_pixel = 16; + + if (var->bits_per_pixel > 8) { + var->red.offset = mode->red_off; + var->red.length = mode->red_len; + var->green.offset = mode->green_off; + var->green.length = mode->green_len; + var->blue.offset = mode->blue_off; + var->blue.length = mode->blue_len; + var->transp.offset = mode->rsvd_off; + var->transp.length = mode->rsvd_len; + + DPRINTK("directcolor: size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", + mode->rsvd_len, + mode->red_len, + mode->green_len, + mode->blue_len, + mode->rsvd_off, + mode->red_off, + mode->green_off, + mode->blue_off); + } else { + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->transp.offset = 0; + + /* We're assuming that we can switch the DAC to 8 bits. If + * this proves to be incorrect, we'll update the fields + * later in set_par(). */ + if (vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC) { + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + } else { + var->red.length = 6; + var->green.length = 6; + var->blue.length = 6; + var->transp.length = 0; + } + } +} + +static void inline vesafb_check_limits(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct fb_videomode *mode; + + if (!var->pixclock) + return; + if (vbe_ib.vbe_version < 0x0300) { + fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, var, info); + return; + } + if (!fb_validate_mode(var, info)) + return; + mode = fb_find_best_mode(var, &info->modelist); + if (mode) { + DPRINTK("find_best_mode: %d %d @ %d (vmode: %d)\n", + mode->xres, mode->yres, mode->refresh, mode->vmode); + if (mode->xres == var->xres && mode->yres == var->yres && + !(mode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE))) { + fb_videomode_to_var(var, mode); + return; + } + } + if (info->monspecs.gtf && !fb_get_mode(FB_MAXTIMINGS, 0, var, info)) + return; + /* Use default refresh rate */ + var->pixclock = 0; +} + +static int vesafb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int match = -1; + int depth = var->red.length + var->green.length + var->blue.length; + + /* Various apps will use bits_per_pixel to set the color depth, + * which is theoretically incorrect, but which we'll try to handle + * here. */ + if (depth == 0 || abs(depth - var->bits_per_pixel) >= 8) + depth = var->bits_per_pixel; + match = vesafb_find_vbe_mode(var->xres, var->yres, depth, + VESAFB_NEED_EXACT_RES); + + if (match == -1) { + DPRINTK("vesafb: mode %dx%d-%d not found\n", var->xres, + var->yres, depth); + return -EINVAL; + } + + vesafb_setup_var(var, info, &vbe_modes[match]); + DPRINTK("found mode 0x%x (%dx%d-%dbpp)\n", + vbe_modes[match].mode_id, vbe_modes[match].x_res, + vbe_modes[match].y_res, vbe_modes[match].depth); + + /* Check whether we have remapped enough memory for this mode. */ + if (var->yres * vbe_modes[match].bytes_per_scan_line > + info->fix.smem_len) { + return -EINVAL; + } + + if ((var->vmode & FB_VMODE_DOUBLE) && + !(vbe_modes[match].mode_attr & 0x100)) + var->vmode &= ~FB_VMODE_DOUBLE; + if ((var->vmode & FB_VMODE_INTERLACED) && + !(vbe_modes[match].mode_attr & 0x200)) + var->vmode &= ~FB_VMODE_INTERLACED; + vesafb_check_limits(var, info); + return 0; +} + +static int vesafb_open(struct fb_info *info, int user) +{ + struct vesafb_task *tsk = NULL; + struct vesafb_par *par = info->par; + int cnt = atomic_read(&par->ref_count); + + if (!cnt) { + vesafb_create_task(tsk); + if (!tsk) + goto out; + + /* Get the VBE state buffer size. We want all available + * hardware state data (CL = 0x0f). */ + tsk->regs.eax = 0x4f04; + tsk->regs.ecx = 0x000f; + tsk->regs.edx = 0x0000; + tsk->flags = TF_CALL; + + if (vesafb_queue_task(tsk)) + goto out; + + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f) { + printk(KERN_WARNING "vesafb: VBE state buffer size " + "cannot be determined (eax: 0x%lx)\n", + tsk->regs.eax); + goto out; + } + + par->vbe_state_size = 64 * (tsk->regs.ebx & 0xffff); + par->vbe_state = kzalloc(par->vbe_state_size, GFP_KERNEL); + if (!par->vbe_state) + goto out; + + vesafb_reset_task(tsk); + tsk->regs.eax = 0x4f04; + tsk->regs.ecx = 0x000f; + tsk->regs.edx = 0x0001; + tsk->flags = TF_CALL | TF_BUF_BX | TF_RETURN_BUF; + tsk->buf = (void*)(par->vbe_state); + tsk->buf_len = par->vbe_state_size; + + if (vesafb_queue_task(tsk)) + goto getstate_failed; + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f) { + printk(KERN_WARNING "vesafb: VBE get state call " + "failed (eax: 0x%lx)\n", tsk->regs.eax); + goto getstate_failed; + } + } +out: + atomic_inc(&par->ref_count); + if (tsk) + kfree(tsk); + return 0; + +getstate_failed: + kfree(par->vbe_state); + par->vbe_state = NULL; + par->vbe_state_size = 0; + goto out; +} + +static int vesafb_release(struct fb_info *info, int user) +{ + struct vesafb_task *tsk = NULL; + struct vesafb_par *par = info->par; + int cnt = atomic_read(&par->ref_count); + + if (!cnt) + return -EINVAL; + + if (cnt == 1 && par->vbe_state && par->vbe_state_size) { + vesafb_create_task(tsk); + if (!tsk) + goto out; + + tsk->regs.eax = 0x0003; + tsk->regs.ebx = 0x0000; + tsk->flags = TF_CALL; + + if (vesafb_queue_task(tsk)) + goto out; + + vesafb_wait_for_task(tsk); + + vesafb_reset_task(tsk); + tsk->regs.eax = 0x4f04; + tsk->regs.ecx = 0x000f; + tsk->regs.edx = 0x0002; + tsk->buf = (void*)(par->vbe_state); + tsk->buf_len = par->vbe_state_size; + tsk->flags = TF_CALL | TF_BUF_BX; + + if (vesafb_queue_task(tsk)) + goto out; + + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f) + printk(KERN_WARNING "vesafb: VBE state restore call " + "failed (eax: 0x%lx)\n", + tsk->regs.eax); + } +out: + atomic_dec(&par->ref_count); + if (tsk) + kfree(tsk); + return 0; +} + +static int __init vesafb_probe(struct platform_device *device); + +static struct fb_ops vesafb_ops = { + .owner = THIS_MODULE, + .fb_open = vesafb_open, + .fb_release = vesafb_release, + .fb_setcolreg = vesafb_setcolreg, + .fb_setcmap = vesafb_setcmap, + .fb_pan_display = vesafb_pan_display, + .fb_blank = vesafb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_check_var = vesafb_check_var, + .fb_set_par = vesafb_set_par +}; + +static struct platform_driver vesafb_driver = { + .probe = vesafb_probe, + .driver = { + .name = "vesafb", + }, +}; + +static struct platform_device *vesafb_device; + +#ifndef MODULE +int __init vesafb_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + DPRINTK("options %s\n",options); + + while ((this_opt = strsep(&options, ",")) != NULL) { + if (!*this_opt) continue; + + DPRINTK("this_opt: %s\n",this_opt); + + if (! strcmp(this_opt, "redraw")) + ypan=0; + else if (! strcmp(this_opt, "ypan")) + ypan=1; + else if (! strcmp(this_opt, "ywrap")) + ypan=2; + else if (! strcmp(this_opt, "vgapal")) + pmi_setpal=0; + else if (! strcmp(this_opt, "pmipal")) + pmi_setpal=1; + else if (! strncmp(this_opt, "mtrr:", 5)) + mtrr = simple_strtoul(this_opt+5, NULL, 0); + else if (! strcmp(this_opt, "nomtrr")) + mtrr=0; + else if (! strcmp(this_opt, "nocrtc")) + nocrtc=1; + else if (! strcmp(this_opt, "noedid")) + noedid=1; + else if (! strcmp(this_opt, "noblank")) + blank=0; + else if (! strcmp(this_opt, "gtf")) + gtf=1; + else if (! strncmp(this_opt, "vtotal:", 7)) + vram_total = simple_strtoul(this_opt + 7, NULL, 0); + else if (! strncmp(this_opt, "vremap:", 7)) + vram_remap = simple_strtoul(this_opt + 7, NULL, 0); + else if (! strncmp(this_opt, "maxhf:", 6)) + maxhf = simple_strtoul(this_opt + 6, NULL, 0); + else if (! strncmp(this_opt, "maxvf:", 6)) + maxvf = simple_strtoul(this_opt + 6, NULL, 0); + else if (! strncmp(this_opt, "maxclk:", 7)) + maxclk = simple_strtoul(this_opt + 7, NULL, 0); + else if (! strncmp(this_opt, "vbemode:", 8)) + vbemode = simple_strtoul(this_opt + 8, NULL,0); + else if (this_opt[0] >= '0' && this_opt[0] <= '9') { + DPRINTK("mode_option: %s\n",this_opt); + mode_option = this_opt; + } else { + printk(KERN_WARNING + "vesafb: unrecognized option %s\n", this_opt); + } + } + + return 0; +} +#endif /* !MODULE */ + +static int vesafb_read_proc_modes(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + int clen = 0, i; + + for (i = 0; i < vbe_modes_cnt; i++) { + clen += min(snprintf(buf + clen, len - clen, "%dx%d-%d\n", vbe_modes[i].x_res, + vbe_modes[i].y_res, vbe_modes[i].depth), len - clen); + } + *eof = 1; + return clen; +} + +static int vesafb_read_proc_vbe_info(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + int clen = 0; + + clen += min(snprintf(buf + clen, len, "Version: %d.%d\n", + ((vbe_ib.vbe_version & 0xff00) >> 8), + vbe_ib.vbe_version & 0xff), len); + clen += min(snprintf(buf + clen, len - clen, "Vendor: %s\n", + (char*)vbe_ib.oem_vendor_name_ptr), len - clen); + clen += min(snprintf(buf + clen, len - clen, "Product: %s\n", + (char*)vbe_ib.oem_product_name_ptr), len - clen); + clen += min(snprintf(buf + clen, len - clen, "OEM rev: %s\n", + (char*)vbe_ib.oem_product_rev_ptr), len - clen); + clen += min(snprintf(buf + clen, len - clen, "OEM string: %s\n", + (char*)vbe_ib.oem_string_ptr), len - clen); + + *eof = 1; + return clen; +} + +static int __init inline vesafb_vbe_getinfo(struct vesafb_task *tsk) +{ + tsk->regs.eax = 0x4f00; + tsk->flags = TF_CALL | TF_GETVBEIB; + tsk->buf = &vbe_ib; + tsk->buf_len = sizeof(vbe_ib); + if (vesafb_queue_task (tsk)) + return -EINVAL; + vesafb_wait_for_task(tsk); + + if (vbe_ib.vbe_version < 0x0200) { + printk(KERN_ERR "vesafb: Sorry, pre-VBE 2.0 cards are " + "not supported.\n"); + return -EINVAL; + } + + if ((tsk->regs.eax & 0xffff) != 0x004f) { + printk(KERN_ERR "vesafb: Getting mode info block failed " + "(eax=0x%x)\n", (u32)tsk->regs.eax); + return -EINVAL; + } + + printk(KERN_INFO "vesafb: %s, %s, %s (OEM: %s)\n", + (char*)vbe_ib.oem_vendor_name_ptr, + (char*)vbe_ib.oem_product_name_ptr, + (char*)vbe_ib.oem_product_rev_ptr, + (char*)vbe_ib.oem_string_ptr); + + printk(KERN_INFO "vesafb: VBE version: %d.%d\n", + ((vbe_ib.vbe_version & 0xff00) >> 8), + vbe_ib.vbe_version & 0xff); + return 0; +} + +static int __init inline vesafb_vbe_getmodes(struct vesafb_task *tsk) +{ + u16 *mode = 0; + int off = 0; + + /* Count available modes. */ + mode = (u16*)vbe_ib.mode_list_ptr; + while (*mode != 0xffff) { + vbe_modes_cnt++; + mode++; + } + + vbe_modes = kmalloc(sizeof(struct vesafb_mode_ib)* + vbe_modes_cnt, GFP_KERNEL); + if (!vbe_modes) + return -ENOMEM; + + /* Get mode info for all available modes. */ + mode = (u16*)vbe_ib.mode_list_ptr; + + while (*mode != 0xffff) { + struct vesafb_mode_ib *mib; + + vesafb_reset_task(tsk); + tsk->regs.eax = 0x4f01; + tsk->regs.ecx = (u32) *mode; + tsk->flags = TF_CALL | TF_RETURN_BUF | TF_BUF_DI; + tsk->buf = vbe_modes+off; + tsk->buf_len = sizeof(struct vesafb_mode_ib); + if (vesafb_queue_task(tsk)) + return -EINVAL; + vesafb_wait_for_task(tsk); + mib = p_mode(tsk->buf); + mib->mode_id = *mode; + + /* We only want modes that are supported with the currennt + * hardware configuration (D0), color (D3), graphics (D4) + * and that have support for the LFB (D7). */ + if ((mib->mode_attr & 0x99) == 0x99 && + mib->bits_per_pixel >= 8) { + off++; + } else { + vbe_modes_cnt--; + } + mode++; + mib->depth = mib->red_len + mib->green_len + mib->blue_len; + /* Handle 8bpp modes and modes with broken color component + * lengths. */ + if (mib->depth == 0 || + (mib->depth == 24 && mib->bits_per_pixel == 32)) + mib->depth = mib->bits_per_pixel; + } + + return 0; +} + +static int __init inline vesafb_vbe_getpmi(struct vesafb_task *tsk) +{ + int i; + + vesafb_reset_task(tsk); + tsk->regs.eax = 0x4f0a; + tsk->regs.ebx = 0x0; + tsk->flags = TF_CALL; + if (vesafb_queue_task(tsk)) + return -EINVAL; + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f || tsk->regs.es < 0xc000) { + pmi_setpal = ypan = 0; + } else { + pmi_base = (u16*)phys_to_virt(((u32)tsk->regs.es << 4) + + tsk->regs.edi); + pmi_start = (void*)((char*)pmi_base + pmi_base[1]); + pmi_pal = (void*)((char*)pmi_base + pmi_base[2]); + printk(KERN_INFO "vesafb: protected mode interface info at " + "%04x:%04x\n", + (u16)tsk->regs.es, (u16)tsk->regs.edi); + printk(KERN_INFO "vesafb: pmi: set display start = %p, " + "set palette = %p\n", pmi_start, pmi_pal); + + if (pmi_base[3]) { + printk(KERN_INFO "vesafb: pmi: ports = "); + for (i = pmi_base[3]/2; pmi_base[i] != 0xffff; i++) + printk("%x ",pmi_base[i]); + printk("\n"); + + /* + * memory areas not supported (yet?) + * + * Rules are: we have to set up a descriptor for the + * requested memory area and pass it in the ES register + * to the BIOS function. + */ + if (pmi_base[i] != 0xffff) { + printk(KERN_INFO "vesafb: can't handle memory " + "requests, pmi disabled\n"); + ypan = pmi_setpal = 0; + } + } + } + return 0; +} + +static int __init inline vesafb_vbe_getedid(struct vesafb_task *tsk, + struct fb_info *info) +{ + int res = 0; + + if (noedid || vbe_ib.vbe_version < 0x0300) + return -EINVAL; + + vesafb_reset_task(tsk); + tsk->regs.eax = 0x4f15; + tsk->regs.ebx = 0; + tsk->regs.ecx = 0; + if (vesafb_queue_task(tsk)) + return -EINVAL; + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) != 0x004f) + return -EINVAL; + + if ((tsk->regs.ebx & 0x3) == 3) { + printk(KERN_INFO "vesafb: VBIOS/hardware supports both " + "DDC1 and DDC2 transfers\n"); + } else if ((tsk->regs.ebx & 0x3) == 2) { + printk(KERN_INFO "vesafb: VBIOS/hardware supports DDC2 " + "transfers\n"); + } else if ((tsk->regs.ebx & 0x3) == 1) { + printk(KERN_INFO "vesafb: VBIOS/hardware supports DDC1 " + "transfers\n"); + } else { + printk(KERN_INFO "vesafb: VBIOS/hardware doesn't support " + "DDC transfers\n"); + return -EINVAL; + } + + vesafb_reset_task(tsk); + tsk->regs.eax = 0x4f15; + tsk->regs.ebx = 1; + tsk->regs.ecx = tsk->regs.edx = 0; + tsk->flags = TF_CALL | TF_RETURN_BUF | TF_BUF_DI; + tsk->buf = kmalloc(EDID_LENGTH, GFP_KERNEL); + tsk->buf_len = EDID_LENGTH; + + if (vesafb_queue_task(tsk)) { + res = -EINVAL; + goto out; + } + vesafb_wait_for_task(tsk); + + if ((tsk->regs.eax & 0xffff) == 0x004f) { + fb_edid_to_monspecs(tsk->buf, &info->monspecs); + fb_videomode_to_modelist(info->monspecs.modedb, + info->monspecs.modedb_len, &info->modelist); + if (info->monspecs.vfmax && info->monspecs.hfmax) { + /* If the maximum pixel clock wasn't specified in + * the EDID block, set it to 300 MHz. */ + if (info->monspecs.dclkmax == 0) + info->monspecs.dclkmax = 300 * 1000000; + info->monspecs.gtf = 1; + } else { + res = -EINVAL; + } + } + +out: kfree(tsk->buf); + return res; +} + +static void __init inline vesafb_vbe_getmonspecs(struct vesafb_task *tsk, + struct fb_info *info) +{ + struct fb_var_screeninfo var; + int i; + memset(&info->monspecs, 0, sizeof(struct fb_monspecs)); + + /* If we didn't get all necessary data from the EDID block, + * mark it as incompatible with the GTF. */ + if (vesafb_vbe_getedid(tsk, info)) + info->monspecs.gtf = 0; + + /* Kernel command line overrides. */ + if (maxclk) + info->monspecs.dclkmax = maxclk * 1000000; + if (maxvf) + info->monspecs.vfmax = maxvf; + if (maxhf) + info->monspecs.hfmax = maxhf * 1000; + + /* In case DDC transfers are not supported the user can provide + * monitor limits manually. Lower limits are set to "safe" values. */ + if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) { + info->monspecs.dclkmin = 0; + info->monspecs.vfmin = 60; + info->monspecs.hfmin = 29000; + info->monspecs.gtf = 1; + } + + if (info->monspecs.gtf) { + printk(KERN_INFO + "vesafb: monitor limits: vf = %d Hz, hf = %d kHz, " + "clk = %d MHz\n", info->monspecs.vfmax, + (int)(info->monspecs.hfmax / 1000), + (int)(info->monspecs.dclkmax / 1000000)); + /* Add valid VESA video modes to our modelist. */ + for (i = 0; i < VESA_MODEDB_SIZE; i++) { + fb_videomode_to_var(&var, (struct fb_videomode *) + &vesa_modes[i]); + if (!fb_validate_mode(&var, info)) + fb_add_videomode((struct fb_videomode *) + &vesa_modes[i], + &info->modelist); + } + } else { + /* Add all VESA video modes to our modelist. */ + fb_videomode_to_modelist((struct fb_videomode *)vesa_modes, + VESA_MODEDB_SIZE, &info->modelist); + printk(KERN_INFO "vesafb: no monitor limits have been set\n"); + } + return; +} + +static int __init inline vesafb_vbe_init(struct fb_info *info) +{ + struct vesafb_task *tsk; + int res = 0; + + vesafb_create_task(tsk); + if (!tsk) + return -EINVAL; + if ((res = vesafb_vbe_getinfo(tsk)) != 0) + goto out; + if ((res = vesafb_vbe_getmodes(tsk)) != 0) + goto out; + if (pmi_setpal || ypan) + vesafb_vbe_getpmi(tsk); + + INIT_LIST_HEAD(&info->modelist); + vesafb_vbe_getmonspecs(tsk, info); + +out: kfree(tsk); + return res; +} + +static int __init decode_mode(u32 *xres, u32 *yres, u32 *bpp, u32 *refresh) +{ + int len = strlen(mode_option), i, err = 0; + u8 res_specified = 0, bpp_specified = 0, refresh_specified = 0, + yres_specified = 0; + + for (i = len-1; i >= 0; i--) { + switch (mode_option[i]) { + case '@': + len = i; + if (!refresh_specified && !bpp_specified && + !yres_specified) { + *refresh = simple_strtoul(&mode_option[i+1], + NULL, 0); + refresh_specified = 1; + } else + goto out; + break; + case '-': + len = i; + if (!bpp_specified && !yres_specified) { + *bpp = simple_strtoul(&mode_option[i+1], + NULL, 0); + bpp_specified = 1; + } else + goto out; + break; + case 'x': + if (!yres_specified) { + *yres = simple_strtoul(&mode_option[i+1], + NULL, 0); + yres_specified = 1; + } else + goto out; + break; + case '0'...'9': + break; + default: + goto out; + } + } + + if (i < 0 && yres_specified) { + *xres = simple_strtoul(mode_option, NULL, 0); + res_specified = 1; + } + +out: if (!res_specified || !yres_specified) { + printk(KERN_ERR "vesafb: invalid resolution, " + "%s not specified\n", + (!res_specified) ? "width" : "height"); + err = -EINVAL; + } + + return err; +} + +static int __init vesafb_init_set_mode(struct fb_info *info) +{ + struct fb_videomode *fbmode; + struct fb_videomode mode; + int i, modeid, refresh = 0; + u8 refresh_specified = 0; + + if (!mode_option) + mode_option = CONFIG_FB_VESA_DEFAULT_MODE; + + if (vbemode > 0) { + for (i = 0; i < vbe_modes_cnt; i++) { + if (vbe_modes[i].mode_id == vbemode) { + info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.sync = FB_SYNC_VERT_HIGH_ACT; + vesafb_setup_var(&info->var, info, + &vbe_modes[i]); + fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, + 60, &info->var, info); + /* With pixclock set to 0, the default BIOS + * timings will be used in set_par(). */ + info->var.pixclock = 0; + modeid = i; + goto out; + } + } + printk(KERN_INFO "specified VBE mode %d not found\n", + vbemode); + vbemode = 0; + } + + /* Decode the mode specified on the kernel command line. We save + * the depth into bits_per_pixel, which is wrong, but will work + * anyway. */ + if (decode_mode(&info->var.xres, &info->var.yres, + &info->var.bits_per_pixel, &refresh)) + return -EINVAL; + if (refresh) + refresh_specified = 1; + else + refresh = 60; + + /* Look for a matching VBE mode. We can live if an exact match + * cannot be found. */ + modeid = vesafb_find_vbe_mode(info->var.xres, info->var.yres, + info->var.bits_per_pixel, 0); + + if (modeid == -1) { + return -EINVAL; + } else { + info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.sync = FB_SYNC_VERT_HIGH_ACT; + vesafb_setup_var(&info->var, info, &vbe_modes[modeid]); + } + if (vbe_ib.vbe_version < 0x0300) { + fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, + &info->var, info); + goto out; + } + if (!gtf) { + struct fb_videomode tmode; + + if (refresh_specified) { + fb_var_to_videomode(&tmode, &info->var); + tmode.refresh = refresh; + fbmode = fb_find_nearest_mode(&tmode, + &info->modelist); + } else + fbmode = fb_find_best_mode(&info->var, + &info->modelist); + + if (fbmode->xres == info->var.xres && + fbmode->yres == info->var.yres && + !(fbmode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE)) + && (!refresh_specified || + abs(refresh - fbmode->refresh) <= 5)) { + fb_videomode_to_var(&info->var, fbmode); + return modeid; + } + } + i = FB_MAXTIMINGS; + if (!info->monspecs.gtf) + i = FB_IGNOREMON | FB_VSYNCTIMINGS; + else if (refresh_specified) + i = FB_VSYNCTIMINGS; + if (!fb_get_mode(i, refresh, &info->var, info)) + goto out; + if (info->monspecs.gtf && + !fb_get_mode(FB_MAXTIMINGS, 0, &info->var, info)) + goto out; + /* Use default refresh rate */ + printk(KERN_WARNING "vesafb: using default BIOS refresh rate\n"); + info->var.pixclock = 0; + +out: + fb_var_to_videomode(&mode, &info->var); + fb_add_videomode(&mode, &info->modelist); + return modeid; +} + +static int __init vesafb_probe(struct platform_device *dev) +{ + char entry[16]; + struct fb_info *info; + struct vesafb_mode_ib *mode = NULL; + int err = 0, i, h; + unsigned int size_vmode; + unsigned int size_remap; + unsigned int size_total; + + vesafb_info = info = framebuffer_alloc(sizeof(struct vesafb_par) + + sizeof(u32) * 256, &dev->dev); + if (!info) + return -ENOMEM; + + if (vesafb_wait_for_thread()) { + printk(KERN_ERR "vesafb: vesafb thread not running\n"); + framebuffer_release(info); + return -EINVAL; + } + + if (vesafb_vbe_init(info)) { + printk(KERN_ERR "vesafb: vbe_init failed\n"); + err = -EINVAL; + goto out; + } + + vesafb_fix.ypanstep = ypan ? 1 : 0; + vesafb_fix.ywrapstep = (ypan>1) ? 1 : 0; + + info->pseudo_palette = ((u8*)info->par + sizeof(struct vesafb_par)); + info->fbops = &vesafb_ops; + info->var = vesafb_defined; + info->fix = vesafb_fix; + + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + err = -ENXIO; + goto out; + } + + i = vesafb_init_set_mode(info); + if (i < 0) { + err = -EINVAL; + goto out_cmap; + } else + mode = &vbe_modes[i]; + + /* Disable blanking if the user requested so. */ + if (!blank) { + info->fbops->fb_blank = NULL; + } + + /* Find out how much IO memory is required for the mode with + * the highest resolution. */ + size_remap = 0; + for (i = 0; i < vbe_modes_cnt; i++) { + h = vbe_modes[i].bytes_per_scan_line * vbe_modes[i].y_res; + if (h > size_remap) + size_remap = h; + } + size_remap *= 2; + + /* size_vmode -- that is the amount of memory needed for the + * used video mode, i.e. the minimum amount of + * memory we need. */ + if (mode != NULL) { + size_vmode = info->var.yres * mode->bytes_per_scan_line; + } else { + size_vmode = info->var.yres * info->var.xres * + ((info->var.bits_per_pixel + 7) >> 3); + } + + /* size_total -- all video memory we have. Used for mtrr + * entries, ressource allocation and bounds + * checking. */ + size_total = vbe_ib.total_memory * 65536; + if (vram_total) + size_total = vram_total * 1024 * 1024; + if (size_total < size_vmode) + size_total = size_vmode; + ((struct vesafb_par*)(info->par))->mem_total = size_total; + + /* size_remap -- the amount of video memory we are going to + * use for vesafb. With modern cards it is no + * option to simply use size_total as th + * wastes plenty of kernel address space. */ + if (vram_remap) + size_remap = vram_remap * 1024 * 1024; + if (size_remap < size_vmode) + size_remap = size_vmode; + if (size_remap > size_total) + size_remap = size_total; + + info->fix.smem_len = size_remap; + info->fix.smem_start = mode->phys_base_ptr; + + /* We have to set it here, because when setup_var() was called, + * smem_len wasn't defined yet. */ + info->var.yres_virtual = info->fix.smem_len / + mode->bytes_per_scan_line; + + if (ypan && info->var.yres_virtual > info->var.yres) { + printk(KERN_INFO "vesafb: scrolling: %s " + "using protected mode interface, " + "yres_virtual=%d\n", + (ypan > 1) ? "ywrap" : "ypan",info->var.yres_virtual); + } else { + printk(KERN_INFO "vesafb: scrolling: redraw\n"); + info->var.yres_virtual = info->var.yres; + ypan = 0; + } + + info->flags = FBINFO_FLAG_DEFAULT | + (ypan) ? FBINFO_HWACCEL_YPAN : 0; + + if (!ypan) + info->fbops->fb_pan_display = NULL; + + if (!request_mem_region(info->fix.smem_start, size_total, "vesafb")) { + printk(KERN_WARNING "vesafb: cannot reserve video memory at " + "0x%lx\n", info->fix.smem_start); + /* We cannot make this fatal. Sometimes this comes from magic + spaces our resource handlers simply don't know about. */ + } + + info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); + + if (!info->screen_base) { + printk(KERN_ERR + "vesafb: abort, cannot ioremap video memory " + "0x%x @ 0x%lx\n", + info->fix.smem_len, info->fix.smem_start); + err = -EIO; + goto out_mem; + } + + /* Request failure does not faze us, as vgacon probably has this + region already (FIXME) */ + request_region(0x3c0, 32, "vesafb"); + +#ifdef CONFIG_MTRR + if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) { + int temp_size = size_total; + unsigned int type = 0; + + switch (mtrr) { + case 1: + type = MTRR_TYPE_UNCACHABLE; + break; + case 2: + type = MTRR_TYPE_WRBACK; + break; + case 3: + type = MTRR_TYPE_WRCOMB; + break; + case 4: + type = MTRR_TYPE_WRTHROUGH; + break; + default: + type = 0; + break; + } + + if (type) { + int rc; + + /* Find the largest power-of-two */ + while (temp_size & (temp_size - 1)) + temp_size &= (temp_size - 1); + + /* Try and find a power of two to add */ + do { + rc = mtrr_add(info->fix.smem_start, + temp_size, type, 1); + temp_size >>= 1; + } while (temp_size >= PAGE_SIZE && rc == -EINVAL); + } + } +#endif /* CONFIG_MTRR */ + + if (register_framebuffer(info) < 0) { + printk(KERN_ERR + "vesafb: failed to register framebuffer device\n"); + err = -EINVAL; + goto out_mem; + } + + printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, " + "using %dk, total %dk\n", info->fix.smem_start, + info->screen_base, size_remap/1024, size_total/1024); + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + sprintf(entry, "fb%d", info->node); + proc_mkdir(entry, 0); + + sprintf(entry, "fb%d/modes", info->node); + create_proc_read_entry(entry, 0, 0, vesafb_read_proc_modes, NULL); + + sprintf(entry, "fb%d/vbe_info", info->node); + create_proc_read_entry(entry, 0, 0, vesafb_read_proc_vbe_info, NULL); + return 0; + +out_mem: + release_mem_region(info->fix.smem_start, size_total); + if (!list_empty(&info->modelist)) + fb_destroy_modelist(&info->modelist); + fb_destroy_modedb(info->monspecs.modedb); +out_cmap: + fb_dealloc_cmap(&info->cmap); +out: + framebuffer_release(info); + vesafb_info = NULL; + kfree(vbe_modes); + vbe_modes = NULL; + return err; +} + +int __init vesafb_init(void) +{ + int ret; +#ifndef MODULE + char *option = NULL; + + if (fb_get_options("vesafb", &option)) + return -ENODEV; + vesafb_setup(option); +#endif + ret = platform_driver_register(&vesafb_driver); + + if (!ret) { + vesafb_device = platform_device_alloc("vesafb", 0); + + if (vesafb_device) + ret = platform_device_add(vesafb_device); + else + ret = -ENOMEM; + + if (ret) { + platform_device_put(vesafb_device); + platform_driver_unregister(&vesafb_driver); + } + } + return ret; +} + +module_init(vesafb_init); + +#ifdef MODULE +void __exit vesafb_exit(void) +{ + char entry[16]; + + if (vesafb_info) + unregister_framebuffer(vesafb_info); + + platform_device_unregister(vesafb_device); + platform_driver_unregister(&vesafb_driver); + + if (vesafb_info) { + struct vesafb_par *par = (struct vesafb_par*)vesafb_info->par; + + sprintf(entry, "fb%d/modes", vesafb_info->node); + remove_proc_entry(entry, NULL); + + sprintf(entry, "fb%d/vbe_info", vesafb_info->node); + remove_proc_entry(entry, NULL); + + sprintf(entry, "fb%d", vesafb_info->node); + remove_proc_entry(entry, NULL); + + iounmap(vesafb_info->screen_base); + release_mem_region(vesafb_info->fix.smem_start, + par->mem_total); + fb_dealloc_cmap(&vesafb_info->cmap); + if (!list_empty(&vesafb_info->modelist)) + fb_destroy_modelist(&vesafb_info->modelist); + fb_destroy_modedb(vesafb_info->monspecs.modedb); + framebuffer_release(vesafb_info); + } + + if (vbe_modes != NULL) + kfree(vbe_modes); +} + +module_exit(vesafb_exit); + +static inline int param_get_scroll(char *buffer, struct kernel_param *kp) +{ + return 0; +} +static inline int param_set_scroll(const char *val, struct kernel_param *kp) +{ + ypan = 0; + + if (! strcmp(val, "redraw")) + ypan = 0; + else if (! strcmp(val, "ypan")) + ypan = 1; + else if (! strcmp(val, "ywrap")) + ypan = 2; + + return 0; +} + +#define param_check_scroll(name, p) __param_check(name, p, void); + +module_param_named(scroll, ypan, scroll, 0); +MODULE_PARM_DESC(scroll,"Scrolling mode, set to 'redraw', 'ypan' or 'ywrap'"); +module_param_named(vgapal, pmi_setpal, invbool, 0); +MODULE_PARM_DESC(vgapal,"bool: set palette using VGA registers"); +module_param_named(pmipal, pmi_setpal, bool, 0); +MODULE_PARM_DESC(pmipal,"bool: set palette using PMI calls"); +module_param(mtrr, uint, 0); +MODULE_PARM_DESC(mtrr,"Memory Type Range Registers setting. Use 0 to disable."); +module_param(blank, bool, 1); +MODULE_PARM_DESC(blank,"bool: enable hardware blanking"); +module_param(nocrtc, bool, 0); +MODULE_PARM_DESC(nocrtc,"bool: ignore CRTC timings when setting modes"); +module_param(noedid, bool, 0); +MODULE_PARM_DESC(noedid,"bool: ignore EDID-provided monitor limits " + "when setting modes"); +module_param(gtf, bool, 0); +MODULE_PARM_DESC(gtf,"bool: force use of VESA GTF to calculate mode timings"); +module_param(vram_remap, uint, 0); +MODULE_PARM_DESC(vram_remap,"Set amount of video memory to be used [MiB]"); +module_param(vram_total, uint, 0); +MODULE_PARM_DESC(vram_total,"Set total amount of video memoery [MiB]"); +module_param(maxclk, ushort, 0); +MODULE_PARM_DESC(maxclk,"Maximum pixelclock [MHz], overrides EDID data"); +module_param(maxhf, ushort, 0); +MODULE_PARM_DESC(maxhf,"Maximum horizontal frequency [kHz], " + "overrides EDID data"); +module_param(maxvf, ushort, 0); +MODULE_PARM_DESC(maxvf,"Maximum vertical frequency [Hz], " + "overrides EDID data"); +module_param_named(mode, mode_option, charp, 0); +MODULE_PARM_DESC(mode, "Specify resolution as " + "\"<xres>x<yres>[-<bpp>][@<refresh>]\""); +module_param(vbemode, ushort, 0); +MODULE_PARM_DESC(vbemode,"VBE mode number to set, overrides 'mode' setting"); + +#endif /* MODULE */ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Januszewski"); +MODULE_DESCRIPTION("Framebuffer driver for VBE2.0+ compliant graphics boards"); + diff --git a/include/linux/sched.h b/include/linux/sched.h index 4463735..7283e48 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1390,6 +1390,8 @@ extern void mmput(struct mm_struct *); extern struct mm_struct *get_task_mm(struct task_struct *task); /* Remove the current tasks stale references to the old mm_struct */ extern void mm_release(struct task_struct *, struct mm_struct *); +/* Create a new mm for a kernel thread */ +extern int set_new_mm(void); extern int copy_thread(int, unsigned long, unsigned long, unsigned long, struct task_struct *, struct pt_regs *); extern void flush_thread(void); diff --git a/include/video/vesa.h b/include/video/vesa.h new file mode 100644 index 0000000..bb5abcf --- /dev/null +++ b/include/video/vesa.h @@ -0,0 +1,150 @@ +#if 0 +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , \ + ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define p_crtc(arg) ((struct vesafb_crtc_ib*)(arg)) +#define p_vbe(arg) ((struct vesafb_vbe_ib*)(arg)) +#define p_mode(arg) ((struct vesafb_mode_ib*)(arg)) + +struct vesafb_task { + u8 flags; + void *buf; + int buf_len; + struct vm86_regs regs; + struct list_head node; + struct completion done; +}; + +/* Vesafb task flags and masks */ +#define TF_CALL 0x00 +#define TF_EXIT 0x01 +#define TF_GETVBEIB 0x02 +#define TF_BUF_DI 0x04 +#define TF_BUF_BX 0x08 +#define TF_RETURN_BUF 0x10 + +/* Macros and functions for manipulating vesafb tasks */ +#define vesafb_create_task(task) \ +do { \ + task = kmalloc(sizeof(struct vesafb_task), GFP_ATOMIC); \ + if (task) \ + memset(task, 0, sizeof(struct vesafb_task)); \ + init_completion(&task->done); \ +} while (0) + +#define vesafb_wait_for_task(task) wait_for_completion(&task->done); +#define vesafb_reset_task(task) init_completion(&task->done); +int vesafb_queue_task(struct vesafb_task *task); + +/* Functions for controlling the vesafb thread */ +int vesafb_wait_for_thread(void); + +#define VBE_CAP_CAN_SWITCH_DAC 0x01 +#define VBE_CAP_VGACOMPAT 0x02 + +/* This struct is 512 bytes long */ +struct vesafb_vbe_ib { + char vbe_signature[4]; + u16 vbe_version; + u32 oem_string_ptr; + u32 capabilities; + u32 mode_list_ptr; + u16 total_memory; + u16 oem_software_rev; + u32 oem_vendor_name_ptr; + u32 oem_product_name_ptr; + u32 oem_product_rev_ptr; + u8 reserved[222]; + char oem_data[256]; +} __attribute__ ((packed)); + +struct vesafb_crtc_ib { + u16 horiz_total; + u16 horiz_start; + u16 horiz_end; + u16 vert_total; + u16 vert_start; + u16 vert_end; + u8 flags; + u32 pixel_clock; + u16 refresh_rate; + u8 reserved[40]; +} __attribute__ ((packed)); + +#define VBE_MODE_VGACOMPAT 0x20 + +struct vesafb_mode_ib { + /* for all VBE revisions */ + u16 mode_attr; + u8 winA_attr; + u8 winB_attr; + u16 win_granularity; + u16 win_size; + u16 winA_seg; + u16 winB_seg; + u32 win_func_ptr; + u16 bytes_per_scan_line; + + /* for VBE 1.2+ */ + u16 x_res; + u16 y_res; + u8 x_char_size; + u8 y_char_size; + u8 planes; + u8 bits_per_pixel; + u8 banks; + u8 memory_model; + u8 bank_size; + u8 image_pages; + u8 reserved1; + + /* Direct color fields for direct/6 and YUV/7 memory models. */ + /* Offsets are bit positions of lsb in the mask. */ + u8 red_len; + u8 red_off; + u8 green_len; + u8 green_off; + u8 blue_len; + u8 blue_off; + u8 rsvd_len; + u8 rsvd_off; + u8 direct_color_info; /* direct color mode attributes */ + + /* for VBE 2.0+ */ + u32 phys_base_ptr; + u8 reserved2[6]; + + /* for VBE 3.0+ */ + u16 lin_bytes_per_scan_line; + u8 bnk_image_pages; + u8 lin_image_pages; + u8 lin_red_len; + u8 lin_red_off; + u8 lin_green_len; + u8 lin_green_off; + u8 lin_blue_len; + u8 lin_blue_off; + u8 lin_rsvd_len; + u8 lin_rsvd_off; + u32 max_pixel_clock; + u16 mode_id; + u8 depth; +} __attribute__ ((packed)); + +struct vesafb_pal_entry { + u_char blue, green, red, pad; +} __attribute__ ((packed)); + +struct vesafb_par { + u8 *vbe_state; + int vbe_state_size; + atomic_t ref_count; + + u32 mem_total; + int mode_idx; + struct vesafb_crtc_ib crtc; +}; + diff --git a/kernel/fork.c b/kernel/fork.c index fc723e5..dc8f93b 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -100,6 +100,7 @@ struct kmem_cache *fs_cachep; /* SLAB cache for vm_area_struct structures */ struct kmem_cache *vm_area_cachep; +EXPORT_SYMBOL_GPL(vm_area_cachep); /* SLAB cache for mm_struct structures (tsk->mm) */ static struct kmem_cache *mm_cachep; @@ -399,6 +400,40 @@ void mmput(struct mm_struct *mm) EXPORT_SYMBOL_GPL(mmput); /** + * set_new_mm - allocate, init and activate a new mm for a kernel thread + */ +int set_new_mm(void) +{ + struct mm_struct *mm; + struct task_struct *tsk = current; + struct mm_struct *active_mm; + + mm = mm_alloc(); + if (!mm) + goto fail_nomem; + if (init_new_context(current,mm)) + goto fail_nocontext; + + task_lock(tsk); + tsk->flags |= PF_BORROWED_MM; + active_mm = tsk->active_mm; + current->mm = mm; + current->active_mm = mm; + activate_mm(active_mm, mm); + task_unlock(current); + + /* Drop the previous active_mm */ + mmdrop(active_mm); + return 0; + +fail_nocontext: + mmdrop(mm); +fail_nomem: + return -EINVAL; +} +EXPORT_SYMBOL_GPL(set_new_mm); + +/** * get_task_mm - acquire a reference to the task's mm * * Returns %NULL if the task has no mm. Checks PF_BORROWED_MM (meaning diff --git a/mm/memory.c b/mm/memory.c index 563792f..a9519ea 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1193,6 +1193,7 @@ int zeromap_page_range(struct vm_area_struct *vma, } while (pgd++, addr = next, addr != end); return err; } +EXPORT_SYMBOL_GPL(zeromap_page_range); pte_t * fastcall get_locked_pte(struct mm_struct *mm, unsigned long addr, spinlock_t **ptl) { diff --git a/mm/mmap.c b/mm/mmap.c index 9717337..6fa5b1c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2024,6 +2024,7 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma) vma_link(mm, vma, prev, rb_link, rb_parent); return 0; } +EXPORT_SYMBOL_GPL(insert_vm_struct); /* * Copy the vma structure to a new location in the same mm,