diff options
author | Marcin Juszkiewicz <hrw@openembedded.org> | 2007-02-06 14:12:48 +0000 |
---|---|---|
committer | Marcin Juszkiewicz <hrw@openembedded.org> | 2007-02-06 14:12:48 +0000 |
commit | c9b1cbf7633375ccc817bceafe669068be7c246a (patch) | |
tree | 6af9ceba7d16b29ac921a9f6432b44edc343533e /packages/linux/linux-rp-2.6.20/vesafb-tng-1.0-rc2-2.6.20-rc2.patch | |
parent | 28dfe9097241db9d7374dd63204dcc1cec27509e (diff) | |
parent | 39dd5539dd04cdf07f8f851854e6cae8e6d8cd2e (diff) |
merge of '64b4c8fe4237a26afc063affc6aa1633bea9dba0'
and 'a4aac9c855570e5ac7a83f7c0536235ba0bbdcaa'
Diffstat (limited to 'packages/linux/linux-rp-2.6.20/vesafb-tng-1.0-rc2-2.6.20-rc2.patch')
-rw-r--r-- | packages/linux/linux-rp-2.6.20/vesafb-tng-1.0-rc2-2.6.20-rc2.patch | 3141 |
1 files changed, 3141 insertions, 0 deletions
diff --git a/packages/linux/linux-rp-2.6.20/vesafb-tng-1.0-rc2-2.6.20-rc2.patch b/packages/linux/linux-rp-2.6.20/vesafb-tng-1.0-rc2-2.6.20-rc2.patch new file mode 100644 index 0000000000..b1b0fc3549 --- /dev/null +++ b/packages/linux/linux-rp-2.6.20/vesafb-tng-1.0-rc2-2.6.20-rc2.patch @@ -0,0 +1,3141 @@ +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, |