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