diff options
author | Koen Kooi <koen@openembedded.org> | 2007-10-23 13:11:51 +0000 |
---|---|---|
committer | Koen Kooi <koen@openembedded.org> | 2007-10-23 13:11:51 +0000 |
commit | 79feff84d18b7b834da765114a892650eb720b77 (patch) | |
tree | 609a4d09038dcd04e3a92f96c175ce46ffe011e2 /packages/linux/linux-rp-2.6.23/uvesafb-0.1-rc3-2.6.22.patch | |
parent | 3d47489f97efa97b856febf65edcd66c1bd48f76 (diff) |
linux-rp: merge .23 and .23+git from poky
Diffstat (limited to 'packages/linux/linux-rp-2.6.23/uvesafb-0.1-rc3-2.6.22.patch')
-rw-r--r-- | packages/linux/linux-rp-2.6.23/uvesafb-0.1-rc3-2.6.22.patch | 2590 |
1 files changed, 2590 insertions, 0 deletions
diff --git a/packages/linux/linux-rp-2.6.23/uvesafb-0.1-rc3-2.6.22.patch b/packages/linux/linux-rp-2.6.23/uvesafb-0.1-rc3-2.6.22.patch new file mode 100644 index 0000000000..711375114f --- /dev/null +++ b/packages/linux/linux-rp-2.6.23/uvesafb-0.1-rc3-2.6.22.patch @@ -0,0 +1,2590 @@ +--- + Documentation/fb/uvesafb.txt | 188 +++ + drivers/video/Kconfig | 18 + drivers/video/Makefile | 1 + drivers/video/modedb.c | 28 + drivers/video/uvesafb.c | 2058 +++++++++++++++++++++++++++++++++++++++++++ + include/linux/connector.h | 7 + include/video/Kbuild | 2 + include/video/uvesafb.h | 193 ++++ + 8 files changed, 2479 insertions(+), 16 deletions(-) + +Index: linux-2.6.22/Documentation/fb/uvesafb.txt +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.22/Documentation/fb/uvesafb.txt 2007-08-28 21:56:34.000000000 +0100 +@@ -0,0 +1,188 @@ ++ ++uvesafb - A Generic Driver for VBE2+ compliant video cards ++========================================================== ++ ++1. Requirements ++--------------- ++ ++uvesafb should work with any video card that has a Video BIOS compliant ++with the VBE 2.0 standard. ++ ++Unlike other drivers, uvesafb makes use of a userspace helper called ++v86d. v86d is used to run the x86 Video BIOS code in a simulated and ++controlled environment. This allows uvesafb to function on arches other ++than x86. Check the v86d documentation for a list of currently supported ++arches. ++ ++v86d source code can be downloaded from the following website: ++ http://dev.gentoo.org/~spock/projects/uvesafb ++ ++Please refer to the v86d documentation for detailed configuration and ++installation instructions. ++ ++Note that the v86d userspace helper has to be available at all times in ++order for uvesafb to work properly. If you want to use uvesafb during ++early boot, you will have to include v86d into an initramfs image, and ++either compile it into the kernel or use it as an initrd. ++ ++2. Caveats and limitations ++-------------------------- ++ ++uvesafb is a _generic_ driver which supports a wide variety of video ++cards, but which is ultimately limited by the Video BIOS interface. ++The most important limitations are: ++ ++- Lack of any type of acceleration. ++- A strict and limited set of supported video modes. Often the native ++ or most optimal resolution/refresh rate for your setup will not work ++ with uvesafb, simply because the Video BIOS doesn't support the ++ video mode you want to use. This can be especially painful with ++ widescreen panels, where native video modes don't have the 4:3 aspect ++ ratio, which is what most BIOS-es are limited to. ++- Adjusting the refresh rate is only possible with a VBE 3.0 compliant ++ Video BIOS. Note that many nVidia Video BIOS-es claim to be VBE 3.0 ++ compliant, while they simply ignore any refresh rate settings. ++ ++3. Configuration ++---------------- ++ ++uvesafb can be compiled either as a module, or directly into the kernel. ++In both cases it supports the same set of configuration options, which ++are either given on the kernel command line or as module parameters, e.g.: ++ ++ video=uvesafb:1024x768-32,mtrr:3,ywrap (compiled into the kernel) ++ ++ # modprobe uvesafb mode=1024x768-32 mtrr=3 scroll=ywrap (module) ++ ++Accepted options: ++ ++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. Available on x86 only. ++ ++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. ++ Available on x86 only. ++ ++redraw Scroll by redrawing the affected part of the screen, this ++ is the safe (and slow) default. ++ ++(If you're using uvesafb as a module, the above three options are ++ used a parameter of the scroll option, e.g. scroll=ypan.) ++ ++vgapal Use the standard VGA registers for palette changes. ++ ++pmipal Use the protected mode interface for palette changes. ++ This is the default if the protected mode interface is ++ available. Available on x86 only. ++ ++mtrr:n Setup memory type range registers for the 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 Do not use memory type range registers. ++ ++vremap:n ++ Remap 'n' MiB of video RAM. If 0 or not specified, remap memory ++ according to video mode. ++ ++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). ++ ++<mode> The mode you want to set, in the standard modedb format. Refer to ++ modedb.txt for a detailed description. When uvesafb is compiled as ++ a module, the mode string should be provided as a value of the ++ 'mode' option. ++ ++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 ++ has any effect only if the Video BIOS is VBE 3.0 compliant. Use it ++ if you have problems with modes set the standard way. Note that ++ using this option implies 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. ++ ++v86d:path ++ Set path to the v86d executable. This option is only available as ++ a module parameter, and not as a part of the video= string. If you ++ need to use it and have uvesafb built into the kernel, use ++ uvesafb.v86d="path". ++ ++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). ++ ++4. The sysfs interface ++---------------------- ++ ++uvesafb provides several sysfs nodes for configurable parameters and ++additional information. ++ ++Driver attributes: ++ ++/sys/bus/platform/drivers/uvesafb ++ - v86d (default: /sbin/v86d) ++ Path to the v86d executable. v86d is started by uvesafb ++ if an instance of the daemon isn't already running. ++ ++Device attributes: ++ ++/sys/bus/platform/drivers/uvesafb/uvesafb.0 ++ - nocrtc ++ Use the default refresh rate (60 Hz) if set to 1. ++ ++ - oem_product_name ++ - oem_product_rev ++ - oem_string ++ - oem_vendor ++ Information about the card and its maker. ++ ++ - vbe_modes ++ A list of video modes supported by the Video BIOS along with their ++ VBE mode numbers in hex. ++ ++ - vbe_version ++ A BCD value indicating the implemented VBE standard. ++ ++5. Miscellaneous ++---------------- ++ ++Uvesafb will set a video mode with the default refresh rate and timings ++from the Video BIOS if you set pixclock to 0 in fb_var_screeninfo. ++ ++ ++-- ++ Michal Januszewski <spock@gentoo.org> ++ Last updated: 2007-06-16 ++ ++ Documentation of the uvesafb options is loosely based on vesafb.txt. ++ +Index: linux-2.6.22/drivers/video/Kconfig +=================================================================== +--- linux-2.6.22.orig/drivers/video/Kconfig 2007-08-28 21:56:33.000000000 +0100 ++++ linux-2.6.22/drivers/video/Kconfig 2007-08-28 21:56:34.000000000 +0100 +@@ -592,6 +592,24 @@ config FB_TGA + + Say Y if you have one of those. + ++config FB_UVESA ++ tristate "Userspace VESA VGA graphics support" ++ depends on FB && CONNECTOR ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ select FB_MODE_HELPERS ++ help ++ This is the frame buffer driver for generic VBE 2.0 compliant ++ graphic cards. It can also take advantage of VBE 3.0 features, ++ such as refresh rate adjustment. ++ ++ This driver generally provides more features than vesafb but ++ requires a userspace helper application called 'v86d'. See ++ <file:Documentation/fb/uvesafb.txt> for more information. ++ ++ If unsure, say N. ++ + config FB_VESA + bool "VESA VGA graphics support" + depends on (FB = y) && X86 +Index: linux-2.6.22/drivers/video/Makefile +=================================================================== +--- linux-2.6.22.orig/drivers/video/Makefile 2007-08-28 21:56:33.000000000 +0100 ++++ linux-2.6.22/drivers/video/Makefile 2007-08-28 21:56:34.000000000 +0100 +@@ -116,6 +116,7 @@ obj-$(CONFIG_FB_XILINX) += xil + obj-$(CONFIG_FB_OMAP) += omap/ + + # Platform or fallback drivers go here ++obj-$(CONFIG_FB_UVESA) += uvesafb.o + obj-$(CONFIG_FB_VESA) += vesafb.o + obj-$(CONFIG_FB_IMAC) += imacfb.o + obj-$(CONFIG_FB_VGA16) += vga16fb.o +Index: linux-2.6.22/drivers/video/modedb.c +=================================================================== +--- linux-2.6.22.orig/drivers/video/modedb.c 2007-08-28 21:54:13.000000000 +0100 ++++ linux-2.6.22/drivers/video/modedb.c 2007-08-28 21:56:34.000000000 +0100 +@@ -606,26 +606,29 @@ done: + DPRINTK("Trying specified video mode%s %ix%i\n", + refresh_specified ? "" : " (ignoring refresh rate)", xres, yres); + +- diff = refresh; ++ if (!refresh_specified) ++ diff = 0; ++ else ++ diff = refresh; ++ + best = -1; + for (i = 0; i < dbsize; i++) { +- if (name_matches(db[i], name, namelen) || +- (res_specified && res_matches(db[i], xres, yres))) { +- if(!fb_try_mode(var, info, &db[i], bpp)) { +- if(!refresh_specified || db[i].refresh == refresh) +- return 1; +- else { +- if(diff > abs(db[i].refresh - refresh)) { +- diff = abs(db[i].refresh - refresh); +- best = i; +- } ++ if ((name_matches(db[i], name, namelen) || ++ (res_specified && res_matches(db[i], xres, yres))) && ++ !fb_try_mode(var, info, &db[i], bpp)) { ++ if (refresh_specified && db[i].refresh == refresh) { ++ return 1; ++ } else { ++ if (diff < db[i].refresh) { ++ diff = db[i].refresh; ++ best = i; + } + } + } + } + if (best != -1) { + fb_try_mode(var, info, &db[best], bpp); +- return 2; ++ return (refresh_specified) ? 2 : 1; + } + + diff = xres + yres; +@@ -938,6 +941,7 @@ void fb_destroy_modelist(struct list_hea + kfree(pos); + } + } ++EXPORT_SYMBOL_GPL(fb_destroy_modelist); + + /** + * fb_videomode_to_modelist: convert mode array to mode list +Index: linux-2.6.22/drivers/video/uvesafb.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.22/drivers/video/uvesafb.c 2007-08-28 21:56:34.000000000 +0100 +@@ -0,0 +1,2058 @@ ++/* ++ * A framebuffer driver for VBE 2.0+ compliant video cards ++ * ++ * (c) 2007 Michal Januszewski <spock@gentoo.org> ++ * Loosely based upon the vesafb driver. ++ * ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/skbuff.h> ++#include <linux/timer.h> ++#include <linux/completion.h> ++#include <linux/connector.h> ++#include <linux/random.h> ++#include <linux/platform_device.h> ++#include <linux/limits.h> ++#include <linux/fb.h> ++#include <linux/io.h> ++#include <linux/mutex.h> ++#include <video/edid.h> ++#include <video/vga.h> ++#include <video/uvesafb.h> ++#ifdef CONFIG_MTRR ++#include <asm/mtrr.h> ++#endif ++#include "edid.h" ++ ++static struct cb_id uvesafb_cn_id = { ++ .idx = CN_IDX_V86D, ++ .val = CN_VAL_V86D_UVESAFB ++}; ++static char v86d_path[PATH_MAX] = "/sbin/v86d"; ++static char v86d_started; /* has v86d been started by uvesafb? */ ++ ++static struct fb_fix_screeninfo uvesafb_fix __devinitdata = { ++ .id = "VESA VGA", ++ .type = FB_TYPE_PACKED_PIXELS, ++ .accel = FB_ACCEL_NONE, ++ .visual = FB_VISUAL_TRUECOLOR, ++}; ++ ++static int mtrr __devinitdata = 3; /* enable mtrr by default */ ++static int blank __devinitdata = 1; /* enable blanking by default */ ++static int ypan __devinitdata = 1; /* 0: scroll, 1: ypan, 2: ywrap */ ++static int pmi_setpal __devinitdata = 1; /* use PMI for palette changes */ ++static int nocrtc __devinitdata; /* ignore CRTC settings */ ++static int noedid __devinitdata; /* don't try DDC transfers */ ++static int vram_remap __devinitdata; /* set amt. of memory to be used */ ++static int vram_total __devinitdata; /* set total amount of memory */ ++static u16 maxclk __devinitdata; /* maximum pixel clock */ ++static u16 maxvf __devinitdata; /* maximum vertical frequency */ ++static u16 maxhf __devinitdata; /* maximum horizontal frequency */ ++static u16 vbemode __devinitdata; /* force use of a specific VBE mode */ ++static char *mode_option __devinitdata; ++ ++static struct uvesafb_ktask *uvfb_tasks[UVESAFB_TASKS_MAX]; ++static DEFINE_MUTEX(uvfb_lock); ++ ++/* ++ * A handler for replies from userspace. ++ * ++ * Make sure each message passes consistency checks and if it does, ++ * find the kernel part of the task struct, copy the registers and ++ * the buffer contents and then complete the task. ++ */ ++static void uvesafb_cn_callback(void *data) ++{ ++ struct cn_msg *msg = data; ++ struct uvesafb_task *utask; ++ struct uvesafb_ktask *task; ++ ++ if (msg->seq >= UVESAFB_TASKS_MAX) ++ return; ++ ++ mutex_lock(&uvfb_lock); ++ task = uvfb_tasks[msg->seq]; ++ ++ if (!task || msg->ack != task->ack) { ++ mutex_unlock(&uvfb_lock); ++ return; ++ } ++ ++ utask = (struct uvesafb_task *)msg->data; ++ ++ /* Sanity checks for the buffer length. */ ++ if (task->t.buf_len < utask->buf_len || ++ utask->buf_len > msg->len - sizeof(*utask)) { ++ mutex_unlock(&uvfb_lock); ++ return; ++ } ++ ++ uvfb_tasks[msg->seq] = NULL; ++ mutex_unlock(&uvfb_lock); ++ ++ memcpy(&task->t, utask, sizeof(*utask)); ++ ++ if (task->t.buf_len && task->buf) ++ memcpy(task->buf, utask + 1, task->t.buf_len); ++ ++ complete(task->done); ++ return; ++} ++ ++static int uvesafb_helper_start(void) ++{ ++ char *envp[] = { ++ "HOME=/", ++ "PATH=/sbin:/bin", ++ NULL, ++ }; ++ ++ char *argv[] = { ++ v86d_path, ++ NULL, ++ }; ++ ++ return call_usermodehelper(v86d_path, argv, envp, 1); ++} ++ ++/* ++ * Execute a uvesafb task. ++ * ++ * Returns 0 if the task is executed successfully. ++ * ++ * A message sent to the userspace consists of the uvesafb_task ++ * struct and (optionally) a buffer. The uvesafb_task struct is ++ * a simplified version of uvesafb_ktask (its kernel counterpart) ++ * containing only the register values, flags and the length of ++ * the buffer. ++ * ++ * Each message is assigned a sequence number (increased linearly) ++ * and a random ack number. The sequence number is used as a key ++ * for the uvfb_tasks array which holds pointers to uvesafb_ktask ++ * structs for all requests. ++ */ ++static int uvesafb_exec(struct uvesafb_ktask *task) ++{ ++ static int seq; ++ struct cn_msg *m; ++ int err; ++ int len = sizeof(task->t) + task->t.buf_len; ++ ++ /* ++ * Check whether the message isn't longer than the maximum ++ * allowed by connector. ++ */ ++ if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) { ++ printk(KERN_WARNING "uvesafb: message too long (%d), " ++ "can't execute task\n", (int)(sizeof(*m) + len)); ++ return -E2BIG; ++ } ++ ++ m = kzalloc(sizeof(*m) + len, GFP_KERNEL); ++ if (!m) ++ return -ENOMEM; ++ ++ init_completion(task->done); ++ ++ memcpy(&m->id, &uvesafb_cn_id, sizeof(m->id)); ++ m->seq = seq; ++ m->len = len; ++ m->ack = random32(); ++ ++ /* uvesafb_task structure */ ++ memcpy(m + 1, &task->t, sizeof(task->t)); ++ ++ /* Buffer */ ++ memcpy((u8 *)(m + 1) + sizeof(task->t), task->buf, task->t.buf_len); ++ ++ /* ++ * Save the message ack number so that we can find the kernel ++ * part of this task when a reply is received from userspace. ++ */ ++ task->ack = m->ack; ++ ++ mutex_lock(&uvfb_lock); ++ ++ /* If all slots are taken -- bail out. */ ++ if (uvfb_tasks[seq]) { ++ mutex_unlock(&uvfb_lock); ++ return -EBUSY; ++ } ++ ++ /* Save a pointer to the kernel part of the task struct. */ ++ uvfb_tasks[seq] = task; ++ mutex_unlock(&uvfb_lock); ++ ++ err = cn_netlink_send(m, 0, gfp_any()); ++ if (err == -ESRCH) { ++ /* ++ * Try to start the userspace helper if sending ++ * the request failed the first time. ++ */ ++ err = uvesafb_helper_start(); ++ if (err) { ++ printk(KERN_ERR "uvesafb: failed to execute %s\n", ++ v86d_path); ++ printk(KERN_ERR "uvesafb: make sure that the v86d " ++ "helper is installed and executable\n"); ++ } else { ++ v86d_started = 1; ++ err = cn_netlink_send(m, 0, gfp_any()); ++ } ++ } ++ kfree(m); ++ ++ if (!err && !(task->t.flags & TF_EXIT)) ++ err = !wait_for_completion_timeout(task->done, ++ msecs_to_jiffies(UVESAFB_TIMEOUT)); ++ ++ mutex_lock(&uvfb_lock); ++ uvfb_tasks[seq] = NULL; ++ mutex_unlock(&uvfb_lock); ++ ++ seq++; ++ if (seq >= UVESAFB_TASKS_MAX) ++ seq = 0; ++ ++ return err; ++} ++ ++/* ++ * Free a uvesafb_ktask struct. ++ */ ++static void uvesafb_free(struct uvesafb_ktask *task) ++{ ++ if (task) { ++ if (task->done) ++ kfree(task->done); ++ kfree(task); ++ } ++} ++ ++/* ++ * Prepare a uvesafb_ktask struct to be used again. ++ */ ++static void uvesafb_reset(struct uvesafb_ktask *task) ++{ ++ struct completion *cpl = task->done; ++ ++ memset(task, 0, sizeof(*task)); ++ task->done = cpl; ++} ++ ++/* ++ * Allocate and prepare a uvesafb_ktask struct. ++ */ ++static struct uvesafb_ktask *uvesafb_prep(void) ++{ ++ struct uvesafb_ktask *task; ++ ++ task = kzalloc(sizeof(*task), GFP_KERNEL); ++ if (task) { ++ task->done = kzalloc(sizeof(*task->done), GFP_KERNEL); ++ if (!task->done) { ++ kfree(task); ++ task = NULL; ++ } ++ } ++ return task; ++} ++ ++static void uvesafb_setup_var(struct fb_var_screeninfo *var, ++ struct fb_info *info, struct vbe_mode_ib *mode) ++{ ++ struct uvesafb_par *par = info->par; ++ ++ var->vmode = FB_VMODE_NONINTERLACED; ++ var->sync = FB_SYNC_VERT_HIGH_ACT; ++ ++ var->xres = mode->x_res; ++ var->yres = mode->y_res; ++ var->xres_virtual = mode->x_res; ++ var->yres_virtual = (par->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; ++ } 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 (par->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 int uvesafb_vbe_find_mode(struct uvesafb_par *par, ++ int xres, int yres, int depth, unsigned char flags) ++{ ++ int i, match = -1, h = 0, d = 0x7fffffff; ++ ++ for (i = 0; i < par->vbe_modes_cnt; i++) { ++ h = abs(par->vbe_modes[i].x_res - xres) + ++ abs(par->vbe_modes[i].y_res - yres) + ++ abs(depth - par->vbe_modes[i].depth); ++ ++ /* ++ * We have an exact match in terms of resolution ++ * and depth. ++ */ ++ if (h == 0) ++ return i; ++ ++ if (h < d || (h == d && par->vbe_modes[i].depth > depth)) { ++ d = h; ++ match = i; ++ } ++ } ++ i = 1; ++ ++ if (flags & UVESAFB_EXACT_DEPTH && ++ par->vbe_modes[match].depth != depth) ++ i = 0; ++ ++ if (flags & UVESAFB_EXACT_RES && d > 24) ++ i = 0; ++ ++ if (i != 0) ++ return match; ++ else ++ return -1; ++} ++ ++static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par) ++{ ++ struct uvesafb_ktask *task; ++ u8 *state; ++ int err; ++ ++ if (!par->vbe_state_size) ++ return NULL; ++ ++ state = kmalloc(par->vbe_state_size, GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ task = uvesafb_prep(); ++ if (!task) { ++ kfree(state); ++ return NULL; ++ } ++ ++ task->t.regs.eax = 0x4f04; ++ task->t.regs.ecx = 0x000f; ++ task->t.regs.edx = 0x0001; ++ task->t.flags = TF_BUF_RET | TF_BUF_ESBX; ++ task->t.buf_len = par->vbe_state_size; ++ task->buf = state; ++ err = uvesafb_exec(task); ++ ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { ++ printk(KERN_WARNING "uvesafb: VBE get state call " ++ "failed (eax=0x%x, err=%d)\n", ++ task->t.regs.eax, err); ++ kfree(state); ++ state = NULL; ++ } ++ ++ uvesafb_free(task); ++ return state; ++} ++ ++static void uvesafb_vbe_state_restore(struct uvesafb_par *par, u8 *state_buf) ++{ ++ struct uvesafb_ktask *task; ++ int err; ++ ++ if (!state_buf) ++ return; ++ ++ task = uvesafb_prep(); ++ if (!task) ++ return; ++ ++ task->t.regs.eax = 0x4f04; ++ task->t.regs.ecx = 0x000f; ++ task->t.regs.edx = 0x0002; ++ task->t.buf_len = par->vbe_state_size; ++ task->t.flags = TF_BUF_ESBX; ++ task->buf = state_buf; ++ ++ err = uvesafb_exec(task); ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) ++ printk(KERN_WARNING "uvesafb: VBE state restore call " ++ "failed (eax=0x%x, err=%d)\n", ++ task->t.regs.eax, err); ++ ++ uvesafb_free(task); ++} ++ ++static int __devinit uvesafb_vbe_getinfo(struct uvesafb_ktask *task, ++ struct uvesafb_par *par) ++{ ++ int err; ++ ++ task->t.regs.eax = 0x4f00; ++ task->t.flags = TF_VBEIB; ++ task->t.buf_len = sizeof(struct vbe_ib); ++ task->buf = &par->vbe_ib; ++ strncpy(par->vbe_ib.vbe_signature, "VBE2", 4); ++ ++ err = uvesafb_exec(task); ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { ++ printk(KERN_ERR "uvesafb: Getting VBE info block failed " ++ "(eax=0x%x, err=%d)\n", (u32)task->t.regs.eax, ++ err); ++ return -EINVAL; ++ } ++ ++ if (par->vbe_ib.vbe_version < 0x0200) { ++ printk(KERN_ERR "uvesafb: Sorry, pre-VBE 2.0 cards are " ++ "not supported.\n"); ++ return -EINVAL; ++ } ++ ++ if (!par->vbe_ib.mode_list_ptr) { ++ printk(KERN_ERR "uvesafb: Missing mode list!\n"); ++ return -EINVAL; ++ } ++ ++ printk(KERN_INFO "uvesafb: "); ++ ++ /* ++ * Convert string pointers and the mode list pointer into ++ * usable addresses. Print informational messages about the ++ * video adapter and its vendor. ++ */ ++ if (par->vbe_ib.oem_vendor_name_ptr) ++ printk("%s, ", ++ ((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr); ++ ++ if (par->vbe_ib.oem_product_name_ptr) ++ printk("%s, ", ++ ((char *)task->buf) + par->vbe_ib.oem_product_name_ptr); ++ ++ if (par->vbe_ib.oem_product_rev_ptr) ++ printk("%s, ", ++ ((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr); ++ ++ if (par->vbe_ib.oem_string_ptr) ++ printk("OEM: %s, ", ++ ((char *)task->buf) + par->vbe_ib.oem_string_ptr); ++ ++ printk("VBE v%d.%d\n", ((par->vbe_ib.vbe_version & 0xff00) >> 8), ++ par->vbe_ib.vbe_version & 0xff); ++ ++ return 0; ++} ++ ++static int __devinit uvesafb_vbe_getmodes(struct uvesafb_ktask *task, ++ struct uvesafb_par *par) ++{ ++ int off = 0, err; ++ u16 *mode; ++ ++ par->vbe_modes_cnt = 0; ++ ++ /* Count available modes. */ ++ mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); ++ while (*mode != 0xffff) { ++ par->vbe_modes_cnt++; ++ mode++; ++ } ++ ++ par->vbe_modes = kzalloc(sizeof(struct vbe_mode_ib) * ++ par->vbe_modes_cnt, GFP_KERNEL); ++ if (!par->vbe_modes) ++ return -ENOMEM; ++ ++ /* Get info about all available modes. */ ++ mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); ++ while (*mode != 0xffff) { ++ struct vbe_mode_ib *mib; ++ ++ uvesafb_reset(task); ++ task->t.regs.eax = 0x4f01; ++ task->t.regs.ecx = (u32) *mode; ++ task->t.flags = TF_BUF_RET | TF_BUF_ESDI; ++ task->t.buf_len = sizeof(struct vbe_mode_ib); ++ task->buf = par->vbe_modes + off; ++ ++ err = uvesafb_exec(task); ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { ++ printk(KERN_ERR "uvesafb: Getting mode info block " ++ "for mode 0x%x failed (eax=0x%x, err=%d)\n", ++ *mode, (u32)task->t.regs.eax, err); ++ return -EINVAL; ++ } ++ ++ mib = task->buf; ++ mib->mode_id = *mode; ++ ++ /* ++ * We only want modes that are supported with the current ++ * hardware configuration, color, graphics and that have ++ * support for the LFB. ++ */ ++ if ((mib->mode_attr & VBE_MODE_MASK) == VBE_MODE_MASK && ++ mib->bits_per_pixel >= 8) ++ off++; ++ else ++ par->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; ++} ++ ++/* ++ * The Protected Mode Interface is 32-bit x86 code, so we only run it on ++ * x86 and not x86_64. ++ */ ++#ifdef CONFIG_X86_32 ++static int __devinit uvesafb_vbe_getpmi(struct uvesafb_ktask *task, ++ struct uvesafb_par *par) ++{ ++ int i, err; ++ ++ uvesafb_reset(task); ++ task->t.regs.eax = 0x4f0a; ++ task->t.regs.ebx = 0x0; ++ err = uvesafb_exec(task); ++ ++ if ((task->t.regs.eax & 0xffff) != 0x4f || task->t.regs.es < 0xc000) { ++ par->pmi_setpal = par->ypan = 0; ++ } else { ++ par->pmi_base = (u16 *)phys_to_virt(((u32)task->t.regs.es << 4) ++ + task->t.regs.edi); ++ par->pmi_start = (u8 *)par->pmi_base + par->pmi_base[1]; ++ par->pmi_pal = (u8 *)par->pmi_base + par->pmi_base[2]; ++ printk(KERN_INFO "uvesafb: protected mode interface info at " ++ "%04x:%04x\n", ++ (u16)task->t.regs.es, (u16)task->t.regs.edi); ++ printk(KERN_INFO "uvesafb: pmi: set display start = %p, " ++ "set palette = %p\n", par->pmi_start, ++ par->pmi_pal); ++ ++ if (par->pmi_base[3]) { ++ printk(KERN_INFO "uvesafb: pmi: ports = "); ++ for (i = par->pmi_base[3]/2; ++ par->pmi_base[i] != 0xffff; i++) ++ printk("%x ", par->pmi_base[i]); ++ printk("\n"); ++ ++ if (par->pmi_base[i] != 0xffff) { ++ printk(KERN_INFO "uvesafb: can't handle memory" ++ " requests, pmi disabled\n"); ++ par->ypan = par->pmi_setpal = 0; ++ } ++ } ++ } ++ return 0; ++} ++#endif /* CONFIG_X86_32 */ ++ ++/* ++ * Check whether a video mode is supported by the Video BIOS and is ++ * compatible with the monitor limits. ++ */ ++static int __devinit uvesafb_is_valid_mode(struct fb_videomode *mode, ++ struct fb_info *info) ++{ ++ if (info->monspecs.gtf) { ++ fb_videomode_to_var(&info->var, mode); ++ if (fb_validate_mode(&info->var, info)) ++ return 0; ++ } ++ ++ if (uvesafb_vbe_find_mode(info->par, mode->xres, mode->yres, 8, ++ UVESAFB_EXACT_RES) == -1) ++ return 0; ++ ++ return 1; ++} ++ ++static int __devinit uvesafb_vbe_getedid(struct uvesafb_ktask *task, ++ struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ int err = 0; ++ ++ if (noedid || par->vbe_ib.vbe_version < 0x0300) ++ return -EINVAL; ++ ++ task->t.regs.eax = 0x4f15; ++ task->t.regs.ebx = 0; ++ task->t.regs.ecx = 0; ++ task->t.buf_len = 0; ++ task->t.flags = 0; ++ ++ err = uvesafb_exec(task); ++ ++ if ((task->t.regs.eax & 0xffff) != 0x004f || err) ++ return -EINVAL; ++ ++ if ((task->t.regs.ebx & 0x3) == 3) { ++ printk(KERN_INFO "uvesafb: VBIOS/hardware supports both " ++ "DDC1 and DDC2 transfers\n"); ++ } else if ((task->t.regs.ebx & 0x3) == 2) { ++ printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC2 " ++ "transfers\n"); ++ } else if ((task->t.regs.ebx & 0x3) == 1) { ++ printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC1 " ++ "transfers\n"); ++ } else { ++ printk(KERN_INFO "uvesafb: VBIOS/hardware doesn't support " ++ "DDC transfers\n"); ++ return -EINVAL; ++ } ++ ++ task->t.regs.eax = 0x4f15; ++ task->t.regs.ebx = 1; ++ task->t.regs.ecx = task->t.regs.edx = 0; ++ task->t.flags = TF_BUF_RET | TF_BUF_ESDI; ++ task->t.buf_len = EDID_LENGTH; ++ task->buf = kzalloc(EDID_LENGTH, GFP_KERNEL); ++ ++ err = uvesafb_exec(task); ++ ++ if ((task->t.regs.eax & 0xffff) == 0x004f && !err) { ++ fb_edid_to_monspecs(task->buf, &info->monspecs); ++ ++ 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 { ++ err = -EINVAL; ++ } ++ ++ kfree(task->buf); ++ return err; ++} ++ ++static void __devinit uvesafb_vbe_getmonspecs(struct uvesafb_ktask *task, ++ struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ int i; ++ ++ memset(&info->monspecs, 0, sizeof(info->monspecs)); ++ ++ /* ++ * If we don't get all necessary data from the EDID block, ++ * mark it as incompatible with the GTF and set nocrtc so ++ * that we always use the default BIOS refresh rate. ++ */ ++ if (uvesafb_vbe_getedid(task, info)) { ++ info->monspecs.gtf = 0; ++ par->nocrtc = 1; ++ } ++ ++ /* 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; ++ par->nocrtc = 0; ++ } ++ ++ if (info->monspecs.gtf) ++ printk(KERN_INFO ++ "uvesafb: 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)); ++ else ++ printk(KERN_INFO "uvesafb: no monitor limits have been set, " ++ "default refresh rate will be used\n"); ++ ++ /* Add VBE modes to the modelist. */ ++ for (i = 0; i < par->vbe_modes_cnt; i++) { ++ struct fb_var_screeninfo var; ++ struct vbe_mode_ib *mode; ++ struct fb_videomode vmode; ++ ++ mode = &par->vbe_modes[i]; ++ memset(&var, 0, sizeof(var)); ++ ++ var.xres = mode->x_res; ++ var.yres = mode->y_res; ++ ++ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, &var, info); ++ fb_var_to_videomode(&vmode, &var); ++ fb_add_videomode(&vmode, &info->modelist); ++ } ++ ++ /* Add valid VESA modes to our modelist. */ ++ for (i = 0; i < VESA_MODEDB_SIZE; i++) { ++ if (uvesafb_is_valid_mode((struct fb_videomode *) ++ &vesa_modes[i], info)) ++ fb_add_videomode(&vesa_modes[i], &info->modelist); ++ } ++ ++ for (i = 0; i < info->monspecs.modedb_len; i++) { ++ if (uvesafb_is_valid_mode(&info->monspecs.modedb[i], info)) ++ fb_add_videomode(&info->monspecs.modedb[i], ++ &info->modelist); ++ } ++ ++ return; ++} ++ ++static void __devinit uvesafb_vbe_getstatesize(struct uvesafb_ktask *task, ++ struct uvesafb_par *par) ++{ ++ int err; ++ ++ uvesafb_reset(task); ++ ++ /* ++ * Get the VBE state buffer size. We want all available ++ * hardware state data (CL = 0x0f). ++ */ ++ task->t.regs.eax = 0x4f04; ++ task->t.regs.ecx = 0x000f; ++ task->t.regs.edx = 0x0000; ++ task->t.flags = 0; ++ ++ err = uvesafb_exec(task); ++ ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { ++ printk(KERN_WARNING "uvesafb: VBE state buffer size " ++ "cannot be determined (eax=0x%x, err=%d)\n", ++ task->t.regs.eax, err); ++ par->vbe_state_size = 0; ++ return; ++ } ++ ++ par->vbe_state_size = 64 * (task->t.regs.ebx & 0xffff); ++} ++ ++static int __devinit uvesafb_vbe_init(struct fb_info *info) ++{ ++ struct uvesafb_ktask *task = NULL; ++ struct uvesafb_par *par = info->par; ++ int err; ++ ++ task = uvesafb_prep(); ++ if (!task) ++ return -ENOMEM; ++ ++ err = uvesafb_vbe_getinfo(task, par); ++ if (err) ++ goto out; ++ ++ err = uvesafb_vbe_getmodes(task, par); ++ if (err) ++ goto out; ++ ++ par->nocrtc = nocrtc; ++#ifdef CONFIG_X86_32 ++ par->pmi_setpal = pmi_setpal; ++ par->ypan = ypan; ++ ++ if (par->pmi_setpal || par->ypan) ++ uvesafb_vbe_getpmi(task, par); ++#else ++ /* The protected mode interface is not available on non-x86. */ ++ par->pmi_setpal = par->ypan = 0; ++#endif ++ ++ INIT_LIST_HEAD(&info->modelist); ++ uvesafb_vbe_getmonspecs(task, info); ++ uvesafb_vbe_getstatesize(task, par); ++ ++out: uvesafb_free(task); ++ return err; ++} ++ ++static int __devinit uvesafb_vbe_init_mode(struct fb_info *info) ++{ ++ struct list_head *pos; ++ struct fb_modelist *modelist; ++ struct fb_videomode *mode; ++ struct uvesafb_par *par = info->par; ++ int i, modeid; ++ ++ /* Has the user requested a specific VESA mode? */ ++ if (vbemode) { ++ for (i = 0; i < par->vbe_modes_cnt; i++) { ++ if (par->vbe_modes[i].mode_id == vbemode) { ++ 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 gotmode; ++ } ++ } ++ printk(KERN_INFO "uvesafb: requested VBE mode 0x%x is " ++ "unavailable\n", vbemode); ++ vbemode = 0; ++ } ++ ++ /* Count the modes in the modelist */ ++ i = 0; ++ list_for_each(pos, &info->modelist) ++ i++; ++ ++ /* ++ * Convert the modelist into a modedb so that we can use it with ++ * fb_find_mode(). ++ */ ++ mode = kzalloc(i * sizeof(*mode), GFP_KERNEL); ++ if (mode) { ++ i = 0; ++ list_for_each(pos, &info->modelist) { ++ modelist = list_entry(pos, struct fb_modelist, list); ++ mode[i] = modelist->mode; ++ i++; ++ } ++ ++ if (!mode_option) ++ mode_option = UVESAFB_DEFAULT_MODE; ++ ++ i = fb_find_mode(&info->var, info, mode_option, mode, i, ++ NULL, 8); ++ ++ kfree(mode); ++ } ++ ++ /* fb_find_mode() failed */ ++ if (i == 0 || i >= 3) { ++ info->var.xres = 640; ++ info->var.yres = 480; ++ mode = (struct fb_videomode *) ++ fb_find_best_mode(&info->var, &info->modelist); ++ ++ if (mode) { ++ fb_videomode_to_var(&info->var, mode); ++ } else { ++ modeid = par->vbe_modes[0].mode_id; ++ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, ++ &info->var, info); ++ goto gotmode; ++ } ++ } ++ ++ /* Look for a matching VBE mode. */ ++ modeid = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, ++ info->var.bits_per_pixel, UVESAFB_EXACT_RES); ++ ++ if (modeid == -1) ++ return -EINVAL; ++ ++gotmode: ++ uvesafb_setup_var(&info->var, info, &par->vbe_modes[modeid]); ++ ++ /* ++ * If we are not VBE3.0+ compliant, we're done -- the BIOS will ++ * ignore our timings anyway. ++ */ ++ if (par->vbe_ib.vbe_version < 0x0300 || par->nocrtc) ++ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, ++ &info->var, info); ++ ++ return modeid; ++} ++ ++static int uvesafb_setpalette(struct uvesafb_pal_entry *entries, int count, ++ int start, struct fb_info *info) ++{ ++ struct uvesafb_ktask *task; ++ struct uvesafb_par *par = info->par; ++ int i = par->mode_idx; ++ int err = 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 < par->vbe_modes_cnt && ++ par->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); ++ } ++ } ++#ifdef CONFIG_X86_32 ++ else if (par->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" (&par->pmi_pal)); /* ESI */ ++ } ++#endif ++ else { ++ task = uvesafb_prep(); ++ if (!task) ++ return -ENOMEM; ++ ++ task->t.regs.eax = 0x4f09; ++ task->t.regs.ebx = 0x0; ++ task->t.regs.ecx = count; ++ task->t.regs.edx = start; ++ task->t.flags = TF_BUF_ESDI; ++ task->t.buf_len = sizeof(struct uvesafb_pal_entry) * count; ++ task->buf = entries; ++ ++ err = uvesafb_exec(task); ++ if ((task->t.regs.eax & 0xffff) != 0x004f) ++ err = 1; ++ ++ uvesafb_free(task); ++ } ++ return err; ++} ++ ++static int uvesafb_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, ++ struct fb_info *info) ++{ ++ struct uvesafb_pal_entry entry; ++ int shift = 16 - info->var.green.length; ++ int err = 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; ++ ++ err = uvesafb_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 err; ++} ++ ++static int uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info) ++{ ++ struct uvesafb_pal_entry *entries; ++ int shift = 16 - info->var.green.length; ++ int i, err = 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 = kmalloc(sizeof(*entries) * cmap->len, GFP_KERNEL); ++ 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; ++ } ++ err = uvesafb_setpalette(entries, cmap->len, cmap->start, info); ++ kfree(entries); ++ } else { ++ /* ++ * For modes with bpp > 8, we only set the pseudo palette in ++ * the fb_info struct. We rely on uvesafb_setcolreg to do all ++ * sanity checking. ++ */ ++ for (i = 0; i < cmap->len; i++) { ++ err |= uvesafb_setcolreg(cmap->start + i, cmap->red[i], ++ cmap->green[i], cmap->blue[i], ++ 0, info); ++ } ++ } ++ return err; ++} ++ ++static int uvesafb_pan_display(struct fb_var_screeninfo *var, ++ struct fb_info *info) ++{ ++#ifdef CONFIG_X86_32 ++ int offset; ++ struct uvesafb_par *par = info->par; ++ ++ 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 (par->pmi_start) { ++ __asm__ __volatile__( ++ "call *(%%edi)" ++ : /* no return value */ ++ : "a" (0x4f07), /* EAX */ ++ "b" (0), /* EBX */ ++ "c" (offset), /* ECX */ ++ "d" (offset >> 16), /* EDX */ ++ "D" (&par->pmi_start)); /* EDI */ ++ } ++#endif ++ return 0; ++} ++ ++static int uvesafb_blank(int blank, struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ struct uvesafb_ktask *task; ++ int err = 1; ++ ++ if (par->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 { ++ task = uvesafb_prep(); ++ if (!task) ++ return -ENOMEM; ++ ++ task->t.regs.eax = 0x4f10; ++ switch (blank) { ++ case FB_BLANK_UNBLANK: ++ task->t.regs.ebx = 0x0001; ++ break; ++ case FB_BLANK_NORMAL: ++ task->t.regs.ebx = 0x0101; /* standby */ ++ break; ++ case FB_BLANK_POWERDOWN: ++ task->t.regs.ebx = 0x0401; /* powerdown */ ++ break; ++ default: ++ goto out; ++ } ++ ++ err = uvesafb_exec(task); ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) ++ err = 1; ++out: uvesafb_free(task); ++ } ++ return err; ++} ++ ++static int uvesafb_open(struct fb_info *info, int user) ++{ ++ struct uvesafb_par *par = info->par; ++ int cnt = atomic_read(&par->ref_count); ++ ++ if (!cnt && par->vbe_state_size) ++ par->vbe_state_orig = uvesafb_vbe_state_save(par); ++ ++ atomic_inc(&par->ref_count); ++ return 0; ++} ++ ++static int uvesafb_release(struct fb_info *info, int user) ++{ ++ struct uvesafb_ktask *task = NULL; ++ struct uvesafb_par *par = info->par; ++ int cnt = atomic_read(&par->ref_count); ++ ++ if (!cnt) ++ return -EINVAL; ++ ++ if (cnt != 1) ++ goto out; ++ ++ task = uvesafb_prep(); ++ if (!task) ++ goto out; ++ ++ /* First, try to set the standard 80x25 text mode. */ ++ task->t.regs.eax = 0x0003; ++ uvesafb_exec(task); ++ ++ /* ++ * Now try to restore whatever hardware state we might have ++ * saved when the fb device was first opened. ++ */ ++ uvesafb_vbe_state_restore(par, par->vbe_state_orig); ++out: ++ atomic_dec(&par->ref_count); ++ if (task) ++ uvesafb_free(task); ++ return 0; ++} ++ ++static int uvesafb_set_par(struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ struct uvesafb_ktask *task = NULL; ++ struct vbe_crtc_ib *crtc = NULL; ++ struct vbe_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 = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, depth, ++ UVESAFB_EXACT_RES | UVESAFB_EXACT_DEPTH); ++ if (i >= 0) ++ mode = &par->vbe_modes[i]; ++ else ++ return -EINVAL; ++ ++ task = uvesafb_prep(); ++ if (!task) ++ return -ENOMEM; ++setmode: ++ task->t.regs.eax = 0x4f02; ++ task->t.regs.ebx = mode->mode_id | 0x4000; /* use LFB */ ++ ++ if (par->vbe_ib.vbe_version >= 0x0300 && !par->nocrtc && ++ info->var.pixclock != 0) { ++ task->t.regs.ebx |= 0x0800; /* use CRTC data */ ++ task->t.flags = TF_BUF_ESDI; ++ crtc = kzalloc(sizeof(struct vbe_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))); ++ ++ 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(*crtc)); ++ } else { ++ memset(&par->crtc, 0, sizeof(*crtc)); ++ } ++ ++ task->t.buf_len = sizeof(struct vbe_crtc_ib); ++ task->buf = &par->crtc; ++ ++ err = uvesafb_exec(task); ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { ++ /* ++ * The mode switch might have failed because we tried to ++ * use our own timings. Try again with the default timings. ++ */ ++ if (crtc != NULL) { ++ printk(KERN_WARNING "uvesafb: mode switch failed " ++ "(eax=0x%x, err=%d). Trying again with " ++ "default timings.\n", task->t.regs.eax, err); ++ uvesafb_reset(task); ++ kfree(crtc); ++ crtc = NULL; ++ info->var.pixclock = 0; ++ goto setmode; ++ } else { ++ printk(KERN_ERR "uvesafb: mode switch failed (eax=" ++ "0x%x, err=%d)\n", task->t.regs.eax, err); ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ par->mode_idx = i; ++ ++ /* For 8bpp modes, always try to set the DAC to 8 bits. */ ++ if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC && ++ mode->bits_per_pixel <= 8) { ++ uvesafb_reset(task); ++ task->t.regs.eax = 0x4f08; ++ task->t.regs.ebx = 0x0800; ++ ++ err = uvesafb_exec(task); ++ if (err || (task->t.regs.eax & 0xffff) != 0x004f || ++ ((task->t.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; ++ ++out: if (crtc != NULL) ++ kfree(crtc); ++ uvesafb_free(task); ++ ++ return err; ++} ++ ++static void uvesafb_check_limits(struct fb_var_screeninfo *var, ++ struct fb_info *info) ++{ ++ const struct fb_videomode *mode; ++ struct uvesafb_par *par = info->par; ++ ++ /* ++ * If pixclock is set to 0, then we're using default BIOS timings ++ * and thus don't have to perform any checks here. ++ */ ++ if (!var->pixclock) ++ return; ++ ++ if (par->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) { ++ 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 uvesafb_check_var(struct fb_var_screeninfo *var, ++ struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ struct vbe_mode_ib *mode = NULL; ++ 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 = uvesafb_vbe_find_mode(par, var->xres, var->yres, depth, ++ UVESAFB_EXACT_RES); ++ if (match == -1) ++ return -EINVAL; ++ ++ mode = &par->vbe_modes[match]; ++ uvesafb_setup_var(var, info, mode); ++ ++ /* ++ * Check whether we have remapped enough memory for this mode. ++ * We might be called at an early stage, when we haven't remapped ++ * any memory yet, in which case we simply skip the check. ++ */ ++ if (var->yres * mode->bytes_per_scan_line > info->fix.smem_len ++ && info->fix.smem_len) ++ return -EINVAL; ++ ++ if ((var->vmode & FB_VMODE_DOUBLE) && ++ !(par->vbe_modes[match].mode_attr & 0x100)) ++ var->vmode &= ~FB_VMODE_DOUBLE; ++ ++ if ((var->vmode & FB_VMODE_INTERLACED) && ++ !(par->vbe_modes[match].mode_attr & 0x200)) ++ var->vmode &= ~FB_VMODE_INTERLACED; ++ ++ uvesafb_check_limits(var, info); ++ ++ var->xres_virtual = var->xres; ++ var->yres_virtual = (par->ypan) ? ++ info->fix.smem_len / mode->bytes_per_scan_line : ++ var->yres; ++ return 0; ++} ++ ++static void uvesafb_save_state(struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ ++ if (par->vbe_state_saved) ++ kfree(par->vbe_state_saved); ++ ++ par->vbe_state_saved = uvesafb_vbe_state_save(par); ++} ++ ++static void uvesafb_restore_state(struct fb_info *info) ++{ ++ struct uvesafb_par *par = info->par; ++ ++ uvesafb_vbe_state_restore(par, par->vbe_state_saved); ++} ++ ++static struct fb_ops uvesafb_ops = { ++ .owner = THIS_MODULE, ++ .fb_open = uvesafb_open, ++ .fb_release = uvesafb_release, ++ .fb_setcolreg = uvesafb_setcolreg, ++ .fb_setcmap = uvesafb_setcmap, ++ .fb_pan_display = uvesafb_pan_display, ++ .fb_blank = uvesafb_blank, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_check_var = uvesafb_check_var, ++ .fb_set_par = uvesafb_set_par, ++ .fb_save_state = uvesafb_save_state, ++ .fb_restore_state = uvesafb_restore_state, ++}; ++ ++static void __devinit uvesafb_init_info(struct fb_info *info, ++ struct vbe_mode_ib *mode) ++{ ++ unsigned int size_vmode; ++ unsigned int size_remap; ++ unsigned int size_total; ++ struct uvesafb_par *par = info->par; ++ int i, h; ++ ++ info->pseudo_palette = ((u8 *)info->par + sizeof(struct uvesafb_par)); ++ info->fix = uvesafb_fix; ++ info->fix.ypanstep = par->ypan ? 1 : 0; ++ info->fix.ywrapstep = (par->ypan > 1) ? 1 : 0; ++ ++ /* ++ * If we were unable to get the state buffer size, disable ++ * functions for saving and restoring the hardware state. ++ */ ++ if (par->vbe_state_size == 0) { ++ info->fbops->fb_save_state = NULL; ++ info->fbops->fb_restore_state = NULL; ++ } ++ ++ /* 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 < par->vbe_modes_cnt; i++) { ++ h = par->vbe_modes[i].bytes_per_scan_line * ++ par->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, resource allocation and bounds ++ * checking. ++ */ ++ size_total = par->vbe_ib.total_memory * 65536; ++ if (vram_total) ++ size_total = vram_total * 1024 * 1024; ++ if (size_total < size_vmode) ++ size_total = size_vmode; ++ ++ /* ++ * 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 yres_virtual 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 (par->ypan && info->var.yres_virtual > info->var.yres) { ++ printk(KERN_INFO "uvesafb: scrolling: %s " ++ "using protected mode interface, " ++ "yres_virtual=%d\n", ++ (par->ypan > 1) ? "ywrap" : "ypan", ++ info->var.yres_virtual); ++ } else { ++ printk(KERN_INFO "uvesafb: scrolling: redraw\n"); ++ info->var.yres_virtual = info->var.yres; ++ par->ypan = 0; ++ } ++ ++ info->flags = FBINFO_FLAG_DEFAULT | ++ (par->ypan) ? FBINFO_HWACCEL_YPAN : 0; ++ ++ if (!par->ypan) ++ info->fbops->fb_pan_display = NULL; ++} ++ ++static void uvesafb_init_mtrr(struct fb_info *info) ++{ ++#ifdef CONFIG_MTRR ++ if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) { ++ int temp_size = info->fix.smem_len; ++ 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 */ ++} ++ ++ ++static ssize_t uvesafb_show_vbe_ver(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ return snprintf(buf, PAGE_SIZE, "%.4x\n", par->vbe_ib.vbe_version); ++} ++ ++static DEVICE_ATTR(vbe_version, S_IRUGO, uvesafb_show_vbe_ver, NULL); ++ ++static ssize_t uvesafb_show_vbe_modes(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ int ret = 0, i; ++ ++ for (i = 0; i < par->vbe_modes_cnt && ret < PAGE_SIZE; i++) { ++ ret += snprintf(buf + ret, PAGE_SIZE - ret, ++ "%dx%d-%d, 0x%.4x\n", ++ par->vbe_modes[i].x_res, par->vbe_modes[i].y_res, ++ par->vbe_modes[i].depth, par->vbe_modes[i].mode_id); ++ } ++ ++ return ret; ++} ++ ++static DEVICE_ATTR(vbe_modes, S_IRUGO, uvesafb_show_vbe_modes, NULL); ++ ++static ssize_t uvesafb_show_vendor(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ if (par->vbe_ib.oem_vendor_name_ptr) ++ return snprintf(buf, PAGE_SIZE, "%s\n", (char *) ++ (&par->vbe_ib) + par->vbe_ib.oem_vendor_name_ptr); ++ else ++ return 0; ++} ++ ++static DEVICE_ATTR(oem_vendor, S_IRUGO, uvesafb_show_vendor, NULL); ++ ++static ssize_t uvesafb_show_product_name(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ if (par->vbe_ib.oem_product_name_ptr) ++ return snprintf(buf, PAGE_SIZE, "%s\n", (char *) ++ (&par->vbe_ib) + par->vbe_ib.oem_product_name_ptr); ++ else ++ return 0; ++} ++ ++static DEVICE_ATTR(oem_product_name, S_IRUGO, uvesafb_show_product_name, NULL); ++ ++static ssize_t uvesafb_show_product_rev(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ if (par->vbe_ib.oem_product_rev_ptr) ++ return snprintf(buf, PAGE_SIZE, "%s\n", (char *) ++ (&par->vbe_ib) + par->vbe_ib.oem_product_rev_ptr); ++ else ++ return 0; ++} ++ ++static DEVICE_ATTR(oem_product_rev, S_IRUGO, uvesafb_show_product_rev, NULL); ++ ++static ssize_t uvesafb_show_oem_string(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ if (par->vbe_ib.oem_string_ptr) ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ (char *)(&par->vbe_ib) + par->vbe_ib.oem_string_ptr); ++ else ++ return 0; ++} ++ ++static DEVICE_ATTR(oem_string, S_IRUGO, uvesafb_show_oem_string, NULL); ++ ++static ssize_t uvesafb_show_nocrtc(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", par->nocrtc); ++} ++ ++static ssize_t uvesafb_store_nocrtc(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); ++ struct uvesafb_par *par = info->par; ++ ++ if (count > 0) { ++ if (buf[0] == '0') ++ par->nocrtc = 0; ++ else ++ par->nocrtc = 1; ++ } ++ return count; ++} ++ ++static DEVICE_ATTR(nocrtc, S_IRUGO | S_IWUSR, uvesafb_show_nocrtc, ++ uvesafb_store_nocrtc); ++ ++static struct attribute *uvesafb_dev_attrs[] = { ++ &dev_attr_vbe_version.attr, ++ &dev_attr_vbe_modes.attr, ++ &dev_attr_oem_vendor.attr, ++ &dev_attr_oem_product_name.attr, ++ &dev_attr_oem_product_rev.attr, ++ &dev_attr_oem_string.attr, ++ &dev_attr_nocrtc.attr, ++ NULL, ++}; ++ ++static struct attribute_group uvesafb_dev_attgrp = { ++ .name = NULL, ++ .attrs = uvesafb_dev_attrs, ++}; ++ ++static int __devinit uvesafb_probe(struct platform_device *dev) ++{ ++ struct fb_info *info; ++ struct vbe_mode_ib *mode = NULL; ++ struct uvesafb_par *par; ++ int err = 0, i; ++ ++ info = framebuffer_alloc(sizeof(*par) + sizeof(u32) * 256, &dev->dev); ++ if (!info) ++ return -ENOMEM; ++ ++ par = info->par; ++ ++ err = uvesafb_vbe_init(info); ++ if (err) { ++ printk(KERN_ERR "uvesafb: vbe_init() failed with %d\n", err); ++ goto out; ++ } ++ ++ info->fbops = &uvesafb_ops; ++ ++ i = uvesafb_vbe_init_mode(info); ++ if (i < 0) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ mode = &par->vbe_modes[i]; ++ } ++ ++ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { ++ err = -ENXIO; ++ goto out; ++ } ++ ++ uvesafb_init_info(info, mode); ++ ++ if (!request_mem_region(info->fix.smem_start, info->fix.smem_len, ++ "uvesafb")) { ++ printk(KERN_ERR "uvesafb: cannot reserve video memory at " ++ "0x%lx\n", info->fix.smem_start); ++ err = -EIO; ++ goto out_mode; ++ } ++ ++ info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); ++ ++ if (!info->screen_base) { ++ printk(KERN_ERR ++ "uvesafb: abort, cannot ioremap 0x%x bytes of video " ++ "memory at 0x%lx\n", ++ info->fix.smem_len, info->fix.smem_start); ++ err = -EIO; ++ goto out_mem; ++ } ++ ++ if (!request_region(0x3c0, 32, "uvesafb")) { ++ printk(KERN_ERR "uvesafb: request region 0x3c0-0x3e0 failed\n"); ++ err = -EIO; ++ goto out_unmap; ++ } ++ ++ uvesafb_init_mtrr(info); ++ platform_set_drvdata(dev, info); ++ ++ if (register_framebuffer(info) < 0) { ++ printk(KERN_ERR ++ "uvesafb: failed to register framebuffer device\n"); ++ err = -EINVAL; ++ goto out_reg; ++ } ++ ++ printk(KERN_INFO "uvesafb: framebuffer at 0x%lx, mapped to 0x%p, " ++ "using %dk, total %dk\n", info->fix.smem_start, ++ info->screen_base, info->fix.smem_len/1024, ++ par->vbe_ib.total_memory * 64); ++ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, ++ info->fix.id); ++ ++ err = sysfs_create_group(&dev->dev.kobj, &uvesafb_dev_attgrp); ++ if (err != 0) ++ printk(KERN_WARNING "fb%d: failed to register attributes\n", ++ info->node); ++ ++ return 0; ++ ++out_reg: ++ release_region(0x3c0, 32); ++out_unmap: ++ iounmap(info->screen_base); ++out_mem: ++ release_mem_region(info->fix.smem_start, info->fix.smem_len); ++out_mode: ++ if (!list_empty(&info->modelist)) ++ fb_destroy_modelist(&info->modelist); ++ fb_destroy_modedb(info->monspecs.modedb); ++ fb_dealloc_cmap(&info->cmap); ++out: ++ if (par->vbe_modes) ++ kfree(par->vbe_modes); ++ ++ framebuffer_release(info); ++ return err; ++} ++ ++static int uvesafb_remove(struct platform_device *dev) ++{ ++ struct fb_info *info = platform_get_drvdata(dev); ++ ++ if (info) { ++ struct uvesafb_par *par = info->par; ++ ++ sysfs_remove_group(&dev->dev.kobj, &uvesafb_dev_attgrp); ++ unregister_framebuffer(info); ++ release_region(0x3c0, 32); ++ iounmap(info->screen_base); ++ release_mem_region(info->fix.smem_start, info->fix.smem_len); ++ fb_destroy_modedb(info->monspecs.modedb); ++ fb_dealloc_cmap(&info->cmap); ++ ++ if (par) { ++ if (par->vbe_modes) ++ kfree(par->vbe_modes); ++ if (par->vbe_state_orig) ++ kfree(par->vbe_state_orig); ++ if (par->vbe_state_saved) ++ kfree(par->vbe_state_saved); ++ } ++ ++ framebuffer_release(info); ++ } ++ return 0; ++} ++ ++static struct platform_driver uvesafb_driver = { ++ .probe = uvesafb_probe, ++ .remove = uvesafb_remove, ++ .driver = { ++ .name = "uvesafb", ++ }, ++}; ++ ++static struct platform_device *uvesafb_device; ++ ++#ifndef MODULE ++static int __devinit uvesafb_setup(char *options) ++{ ++ char *this_opt; ++ ++ if (!options || !*options) ++ return 0; ++ ++ while ((this_opt = strsep(&options, ",")) != NULL) { ++ if (!*this_opt) continue; ++ ++ 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 (!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') { ++ mode_option = this_opt; ++ } else { ++ printk(KERN_WARNING ++ "uvesafb: unrecognized option %s\n", this_opt); ++ } ++ } ++ ++ return 0; ++} ++#endif /* !MODULE */ ++ ++static ssize_t show_v86d(struct device_driver *dev, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", v86d_path); ++} ++ ++static ssize_t store_v86d(struct device_driver *dev, const char *buf, ++ size_t count) ++{ ++ strncpy(v86d_path, buf, PATH_MAX); ++ return count; ++} ++ ++static DRIVER_ATTR(v86d, S_IRUGO | S_IWUSR, show_v86d, store_v86d); ++ ++static int __devinit uvesafb_init(void) ++{ ++ int err; ++ ++#ifndef MODULE ++ char *option = NULL; ++ ++ if (fb_get_options("uvesafb", &option)) ++ return -ENODEV; ++ uvesafb_setup(option); ++#endif ++ err = cn_add_callback(&uvesafb_cn_id, "uvesafb", uvesafb_cn_callback); ++ if (err) ++ return err; ++ ++ err = platform_driver_register(&uvesafb_driver); ++ ++ if (!err) { ++ uvesafb_device = platform_device_alloc("uvesafb", 0); ++ if (uvesafb_device) ++ err = platform_device_add(uvesafb_device); ++ else ++ err = -ENOMEM; ++ ++ if (err) { ++ platform_device_put(uvesafb_device); ++ platform_driver_unregister(&uvesafb_driver); ++ cn_del_callback(&uvesafb_cn_id); ++ return err; ++ } ++ ++ err = driver_create_file(&uvesafb_driver.driver, ++ &driver_attr_v86d); ++ if (err) { ++ printk(KERN_WARNING "uvesafb: failed to register " ++ "attributes\n"); ++ err = 0; ++ } ++ } ++ return err; ++} ++ ++module_init(uvesafb_init); ++ ++static void __devexit uvesafb_exit(void) ++{ ++ struct uvesafb_ktask *task; ++ ++ if (v86d_started) { ++ task = uvesafb_prep(); ++ if (task) { ++ task->t.flags = TF_EXIT; ++ uvesafb_exec(task); ++ uvesafb_free(task); ++ } ++ } ++ ++ cn_del_callback(&uvesafb_cn_id); ++ driver_remove_file(&uvesafb_driver.driver, &driver_attr_v86d); ++ platform_device_unregister(uvesafb_device); ++ platform_driver_unregister(&uvesafb_driver); ++} ++ ++module_exit(uvesafb_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, "Set palette using VGA registers"); ++module_param_named(pmipal, pmi_setpal, bool, 0); ++MODULE_PARM_DESC(pmipal, "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, 0); ++MODULE_PARM_DESC(blank, "Enable hardware blanking"); ++module_param(nocrtc, bool, 0); ++MODULE_PARM_DESC(nocrtc, "Ignore CRTC timings when setting modes"); ++module_param(noedid, bool, 0); ++MODULE_PARM_DESC(noedid, ++ "Ignore EDID-provided monitor limits when setting modes"); ++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 initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); ++module_param(vbemode, ushort, 0); ++MODULE_PARM_DESC(vbemode, ++ "VBE mode number to set, overrides the 'mode' option"); ++module_param_string(v86d, v86d_path, PATH_MAX, 0660); ++MODULE_PARM_DESC(v86d, "Path to the v86d userspace helper."); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Michal Januszewski <spock@gentoo.org>"); ++MODULE_DESCRIPTION("Framebuffer driver for VBE2.0+ compliant graphics boards"); ++ +Index: linux-2.6.22/include/linux/connector.h +=================================================================== +--- linux-2.6.22.orig/include/linux/connector.h 2007-08-28 21:54:13.000000000 +0100 ++++ linux-2.6.22/include/linux/connector.h 2007-08-28 21:56:34.000000000 +0100 +@@ -36,14 +36,15 @@ + #define CN_VAL_CIFS 0x1 + #define CN_W1_IDX 0x3 /* w1 communication */ + #define CN_W1_VAL 0x1 ++#define CN_IDX_V86D 0x4 ++#define CN_VAL_V86D_UVESAFB 0x1 + +- +-#define CN_NETLINK_USERS 4 ++#define CN_NETLINK_USERS 5 + + /* + * Maximum connector's message size. + */ +-#define CONNECTOR_MAX_MSG_SIZE 1024 ++#define CONNECTOR_MAX_MSG_SIZE 16384 + + /* + * idx and val are unique identifiers which +Index: linux-2.6.22/include/video/Kbuild +=================================================================== +--- linux-2.6.22.orig/include/video/Kbuild 2007-08-28 21:54:13.000000000 +0100 ++++ linux-2.6.22/include/video/Kbuild 2007-08-28 21:56:34.000000000 +0100 +@@ -1 +1 @@ +-unifdef-y += sisfb.h ++unifdef-y += sisfb.h uvesafb.h +Index: linux-2.6.22/include/video/uvesafb.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.22/include/video/uvesafb.h 2007-08-28 21:56:34.000000000 +0100 +@@ -0,0 +1,193 @@ ++#ifndef _UVESAFB_H ++#define _UVESAFB_H ++ ++struct v86_regs { ++ __u32 ebx; ++ __u32 ecx; ++ __u32 edx; ++ __u32 esi; ++ __u32 edi; ++ __u32 ebp; ++ __u32 eax; ++ __u32 eip; ++ __u32 eflags; ++ __u32 esp; ++ __u16 cs; ++ __u16 ss; ++ __u16 es; ++ __u16 ds; ++ __u16 fs; ++ __u16 gs; ++}; ++ ++/* Task flags */ ++#define TF_VBEIB 0x01 ++#define TF_BUF_ESDI 0x02 ++#define TF_BUF_ESBX 0x04 ++#define TF_BUF_RET 0x08 ++#define TF_EXIT 0x10 ++ ++struct uvesafb_task { ++ __u8 flags; ++ int buf_len; ++ struct v86_regs regs; ++}; ++ ++/* Constants for the capabilities field ++ * in vbe_ib */ ++#define VBE_CAP_CAN_SWITCH_DAC 0x01 ++#define VBE_CAP_VGACOMPAT 0x02 ++ ++/* The VBE Info Block */ ++struct 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]; ++ char misc_data[512]; ++} __attribute__ ((packed)); ++ ++#ifdef __KERNEL__ ++ ++/* VBE CRTC Info Block */ ++struct vbe_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 ++#define VBE_MODE_COLOR 0x08 ++#define VBE_MODE_SUPPORTEDHW 0x01 ++#define VBE_MODE_GRAPHICS 0x10 ++#define VBE_MODE_LFB 0x80 ++ ++#define VBE_MODE_MASK (VBE_MODE_COLOR | VBE_MODE_SUPPORTEDHW | \ ++ VBE_MODE_GRAPHICS | VBE_MODE_LFB) ++ ++/* VBE Mode Info Block */ ++struct vbe_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)); ++ ++#define UVESAFB_DEFAULT_MODE "640x480-16" ++ ++/* How long to wait for a reply from userspace [ms] */ ++#define UVESAFB_TIMEOUT 5000 ++ ++/* Max number of concurrent tasks */ ++#define UVESAFB_TASKS_MAX 16 ++ ++#define dac_reg (0x3c8) ++#define dac_val (0x3c9) ++ ++struct uvesafb_pal_entry { ++ u_char blue, green, red, pad; ++} __attribute__ ((packed)); ++ ++struct uvesafb_ktask { ++ struct uvesafb_task t; ++ void *buf; ++ struct completion *done; ++ u32 ack; ++}; ++ ++static int uvesafb_exec(struct uvesafb_ktask *tsk); ++ ++#define UVESAFB_EXACT_RES 1 ++#define UVESAFB_EXACT_DEPTH 2 ++ ++struct uvesafb_par { ++ struct vbe_ib vbe_ib; /* VBE Info Block */ ++ struct vbe_mode_ib *vbe_modes; /* list of supported VBE modes */ ++ int vbe_modes_cnt; ++ ++ u8 nocrtc; ++ u8 ypan; /* 0 - nothing, 1 - ypan, 2 - ywrap */ ++ u8 pmi_setpal; /* PMI for palette changes */ ++ u16 *pmi_base; /* protected mode interface location */ ++ void *pmi_start; ++ void *pmi_pal; ++ u8 *vbe_state_orig; /* ++ * original hardware state, before the ++ * driver was loaded ++ */ ++ u8 *vbe_state_saved; /* state saved by fb_save_state */ ++ int vbe_state_size; ++ atomic_t ref_count; ++ ++ int mode_idx; ++ struct vbe_crtc_ib crtc; ++}; ++ ++#endif /* __KERNEL__ */ ++#endif /* _UVESAFB_H */ |