Add vesafb-tng patch to linux-openzaurus.
authorChris Larson <kergoth@openedhand.com>
Mon, 16 Oct 2006 16:14:33 +0000 (16:14 +0000)
committerChris Larson <kergoth@openedhand.com>
Mon, 16 Oct 2006 16:14:33 +0000 (16:14 +0000)
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@801 311d38ba-8fff-0310-9ca6-ca027cbcb966

meta/packages/linux/linux-openzaurus-2.6.17/defconfig-qemux86
meta/packages/linux/linux-openzaurus-2.6.17/vesafb-tng-1.0-rc2-git-20060629.patch [new file with mode: 0644]
meta/packages/linux/linux-openzaurus_2.6.17.bb

index 1a58023..b991075 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.17
-# Wed Aug 23 11:24:17 2006
+# Mon Oct 16 19:42:42 2006
 #
 CONFIG_X86_32=y
 CONFIG_SEMAPHORE_SLEEPERS=y
@@ -990,7 +990,7 @@ CONFIG_FB_CFB_COPYAREA=y
 CONFIG_FB_CFB_IMAGEBLIT=y
 # CONFIG_FB_MACMODES is not set
 CONFIG_FB_FIRMWARE_EDID=y
-# CONFIG_FB_MODE_HELPERS is not set
+CONFIG_FB_MODE_HELPERS=y
 # CONFIG_FB_TILEBLITTING is not set
 # CONFIG_FB_CIRRUS is not set
 # CONFIG_FB_PM2 is not set
@@ -1000,6 +1000,9 @@ CONFIG_FB_FIRMWARE_EDID=y
 # CONFIG_FB_IMSTT is not set
 CONFIG_FB_VGA16=y
 CONFIG_FB_VESA=y
+# CONFIG_FB_VESA_STD is not set
+CONFIG_FB_VESA_TNG=y
+CONFIG_FB_VESA_DEFAULT_MODE="640x480-32@60"
 CONFIG_VIDEO_SELECT=y
 # CONFIG_FB_HGA is not set
 # CONFIG_FB_S1D13XXX is not set
diff --git a/meta/packages/linux/linux-openzaurus-2.6.17/vesafb-tng-1.0-rc2-git-20060629.patch b/meta/packages/linux/linux-openzaurus-2.6.17/vesafb-tng-1.0-rc2-git-20060629.patch
new file mode 100644 (file)
index 0000000..188ce60
--- /dev/null
@@ -0,0 +1,3089 @@
+# Patch generated against a6047eef1c465c38aacfbdab193161b3f0cd144
+---
+# Documentation/fb/vesafb.txt   |  250 ++++--
+# arch/i386/boot/video.S        |   12 
+# drivers/video/Kconfig         |   56 +
+# drivers/video/Makefile        |    6 
+# drivers/video/fbmem.c         |    1 
+# drivers/video/modedb.c        |    1 
+# drivers/video/vesafb-thread.c |  727 +++++++++++++++++++
+# drivers/video/vesafb-tng.c    | 1598 ++++++++++++++++++++++++++++++++++++++++++
+# include/linux/sched.h         |    2 
+# include/video/vesa.h          |  150 +++
+# kernel/fork.c                 |   35 
+# mm/memory.c                   |    1 
+# mm/mmap.c                     |    1 
+# 13 files changed, 2748 insertions(+), 92 deletions(-)
+#
+--- linux-2.6.17.orig/Documentation/fb/vesafb.txt
++++ linux-2.6.17/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
+     | 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
+ 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
+ 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.
+- * 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
++If you want to use the default BIOS refresh rate while switching modes
++on a running system, set pixclock to 0.
++
++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.
++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"
++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"
+-Accepted options:
++Note that vesafb-tng still uses the "video=vesafb:option" format of the
++kernel command line video parameter. "video=vesafb-tng:xxx" is incorrect.
+-invers        no comment...
++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
+-      kontra: * scrolling only parts of the screen causes some
+-                ugly flicker effects (boot logo flickers for
+-                example).
++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.
++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.
++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.
+-vgapal        Use the standard vga registers for palette changes.
+-      This is the default.
+-pmipal        Use the protected mode interface 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
++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".
++        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:
+-  Gerd
++<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).
++
++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>
++
+--- linux-2.6.17.orig/arch/i386/boot/video.S
++++ linux-2.6.17/arch/i386/boot/video.S
+@@ -165,10 +165,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
+@@ -201,6 +203,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
+@@ -283,6 +286,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:
+@@ -497,10 +501,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
+       
+@@ -572,6 +578,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
+@@ -605,6 +612,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
+--- linux-2.6.17.orig/drivers/video/Kconfig
++++ linux-2.6.17/drivers/video/Kconfig
+@@ -472,8 +472,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
+@@ -481,7 +495,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 the frame buffer device driver for generic VESA 2.0 
++        compliant graphic cards. It is capable of taking advantage of 
++        VBE 3.0 features. With this driver you will be able to adjust
++        the refresh rate (VBE 3.0 compliant boards only) and change
++        the graphic mode on-the-fly.
++        
++        You will also get a boot time penguin logo at no additional cost. Please
++        read <file:Documentation/fb/vesafb.txt>.
++
++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 VIDEO_SELECT
+       bool
+--- linux-2.6.17.orig/drivers/video/Makefile
++++ linux-2.6.17/drivers/video/Makefile
+@@ -97,7 +97,11 @@ obj-$(CONFIG_FB_IMX)              += imx
+ obj-$(CONFIG_FB_S3C2410)        += s3c2410fb.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_VGA16)            += vga16fb.o vgastate.o
+ obj-$(CONFIG_FB_OF)               += offb.o
+--- linux-2.6.17.orig/drivers/video/fbmem.c
++++ linux-2.6.17/drivers/video/fbmem.c
+@@ -1438,6 +1438,7 @@ fbmem_init(void)
+               printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
+               fb_class = NULL;
+       }
++
+       return 0;
+ }
+--- linux-2.6.17.orig/drivers/video/modedb.c
++++ linux-2.6.17/drivers/video/modedb.c
+@@ -671,6 +671,7 @@ void fb_var_to_videomode(struct fb_video
+ {
+       u32 pixclock, hfreq, htotal, vtotal;
++      mode->refresh = 0;
+       mode->name = NULL;
+       mode->xres = var->xres;
+       mode->yres = var->yres;
+--- /dev/null
++++ linux-2.6.17/drivers/video/vesafb-thread.c
+@@ -0,0 +1,727 @@
++/*
++ * Framebuffer driver for VBE 2.0+ compliant graphic boards.
++ * Kernel thread and vm86 routines.
++ *
++ * (c) 2004-2006 Michal Januszewski <spock@gentoo.org>
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/slab.h>
++#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/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"
++
++#ifdef MODULE
++int errno;
++#endif
++
++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;
++
++_syscall3(int,ioperm,unsigned long, a, unsigned long, b, unsigned long, c);
++_syscall1(int,vm86old,struct vm86_struct __user*, v86);
++
++#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)
++
++/* 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, SLAB_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");
++
+--- /dev/null
++++ linux-2.6.17/drivers/video/vesafb-tng.c
+@@ -0,0 +1,1598 @@
++/*
++ * 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 += sprintf(buf + clen, "%dx%d-%d\n", vbe_modes[i].x_res,
++                              vbe_modes[i].y_res, vbe_modes[i].depth);
++      }
++      *start = buf + offset;
++
++      if (clen > offset) {
++              clen -= offset;
++      } else {
++              clen = 0;
++      }
++      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 += sprintf(buf + clen, "Version:    %d.%d\n",
++                      ((vbe_ib.vbe_version & 0xff00) >> 8),
++                      vbe_ib.vbe_version & 0xff);
++      clen += sprintf(buf + clen, "Vendor:     %s\n",
++                      (char*)vbe_ib.oem_vendor_name_ptr);
++      clen += sprintf(buf + clen, "Product:    %s\n",
++                      (char*)vbe_ib.oem_product_name_ptr);
++      clen += sprintf(buf + clen, "OEM rev:    %s\n",
++                      (char*)vbe_ib.oem_product_rev_ptr);
++      clen += sprintf(buf + clen, "OEM string: %s\n",
++                      (char*)vbe_ib.oem_string_ptr);
++
++      *start = buf + offset;
++
++      if (clen > offset) {
++              clen -= offset;
++      } else {
++              clen = 0;
++      }
++      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_named(nomtrr, mtrr, invbool, 0);
++MODULE_PARM_DESC(nomtrr,"bool: disable use of MTRR registers");
++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");
++
+--- linux-2.6.17.orig/include/linux/sched.h
++++ linux-2.6.17/include/linux/sched.h
+@@ -1159,6 +1159,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);
+--- /dev/null
++++ linux-2.6.17/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;
++};
++
+--- linux-2.6.17.orig/kernel/fork.c
++++ linux-2.6.17/kernel/fork.c
+@@ -97,6 +97,7 @@ kmem_cache_t *fs_cachep;
+ /* SLAB cache for vm_area_struct structures */
+ kmem_cache_t *vm_area_cachep;
++EXPORT_SYMBOL_GPL(vm_area_cachep);
+ /* SLAB cache for mm_struct structures (tsk->mm) */
+ static kmem_cache_t *mm_cachep;
+@@ -383,6 +384,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
+--- linux-2.6.17.orig/mm/memory.c
++++ linux-2.6.17/mm/memory.c
+@@ -1162,6 +1162,7 @@ int zeromap_page_range(struct vm_area_st
+       } 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)
+ {
+--- linux-2.6.17.orig/mm/mmap.c
++++ linux-2.6.17/mm/mmap.c
+@@ -1996,6 +1996,7 @@ int insert_vm_struct(struct mm_struct * 
+       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,
index f5460ae..80d7a72 100644 (file)
@@ -1,6 +1,6 @@
 require linux-openzaurus.inc
 
-PR = "r27"
+PR = "r28"
 
 # Handy URLs
 # git://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \
@@ -63,6 +63,7 @@ SRC_URI = "http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.17.tar.bz2 \
            file://connectplus-remove-ide-HACK.patch;patch=1;status=hack \
            file://orinoco-remove-all-which-are-in-hostap-HACK.patch;patch=1;status=unmergable-hack \
            file://squashfs3.0-2.6.15.patch;patch=1;status=external \
+           file://vesafb-tng-1.0-rc2-git-20060629.patch;patch=1;status=external \
            file://defconfig-c7x0 \
            file://defconfig-ipaq-pxa270 \
            file://defconfig-collie \