Hi all,
 
This is a patch submission I'm making for my
framebuffer Enhanced functionality module,
whose purpose is to expand the range of
graphics capabilities available within the kernel,
and later available to userspace via my FBUI
project (fbui.org). I also plan to use Enhanced to
support an anti-aliased console font feature sometime
in the near future. The generic graphics routines
that I've written are taken from FBUI and are
therefore written for use with vesafb; but they should
work with other drivers.
 
I've done this as a "diff -Naur" patch to
kernel 2.6.15.4. If you need something else
let me know.
 
I've also placed the patch here:
http://home.comcast.net/~fbui/Enhanced_0.txt
 
Zack T. Smith
 
diff -Naur linux-2.6.15.4-original/drivers/video/Kconfig linux-2.6.15.4/drivers/video/Kconfig
--- linux-2.6.15.4-original/drivers/video/Kconfig 2006-02-10 07:22:48.000000000 +0000
+++ linux-2.6.15.4/drivers/video/Kconfig 2006-02-26 12:15:19.000000000 +0000
@@ -38,6 +38,64 @@
    (e.g. an accelerated X server) and that are not frame buffer
    device-aware may cause unexpected results. If unsure, say N.
 
+config FB_ENHANCED
+        tristate "Enhanced Functionality for a generic framebuffer"
+        depends on FB
+        default y
+        ---help---
+          Enhanced Functionality is a compact and robust set of routines
+   derived from the FramebufferUI project (see http://fbui.org).
+
+   They implement:
+  + drawing routines that are clippable and
+    which support pixel-level transparency (alpha blending)
+   - line draw
+   - rectangle fill
+   - triangle fill
+                + robust and optimized clippable image blit from userspace
+    and kernel memory, supporting images of these types:
+   - opaque 16, 24, 32 bpp
+   - 1 bpp bitmap
+   - 8 bit alpha with foreground color
+  + robust copy area, also clippable, supporting
+    up, down, left and right directions, currently
+    supporting 16/24/32 bpp.
+
+          In future, this code will be used to permit anti-aliased
+          console fonts and will be necessary for the next FramebufferUI.
+
+          Enhanced Functionality provides the paradigm for future
+          hardware-based accelerated versions of the functionality
+          it provides.
+
+config FB_ENHANCED_32BPP
+        bool "Support 32 bits per pixel drawing"
+        depends on FB_ENHANCED
+        default n
+        help
+   32bpp (8 red, 8 green, 8 blue). This offers
+   good color realism however it is often slower
+   than 16bpp.
+
+config FB_ENHANCED_24BPP
+        bool "Support 24 bits per pixel drawing"
+        depends on FB_ENHANCED
+        default n
+        help
+          24bpp drawing. It's memory-efficient but
+          in VESA it will probably always be slower
+          than either 16bpp or 32bpp.
+
+config FB_ENHANCED_16BPP
+        bool "Support 16 bits per pixel drawing"
+        depends on FB_ENHANCED
+        default y
+        help
+          16bpp mode uses 5 bits for red, 6 for green,
+          and 5 for blue. In VESA mode, this depth
+          is sometimes twice as fast as 32bpp, other
+          times slower.
+
 config FB_CFB_FILLRECT
  tristate
  depends on FB
diff -Naur linux-2.6.15.4-original/drivers/video/Makefile linux-2.6.15.4/drivers/video/Makefile
--- linux-2.6.15.4-original/drivers/video/Makefile 2006-02-10 07:22:48.000000000 +0000
+++ linux-2.6.15.4/drivers/video/Makefile 2006-02-24 12:59:50.000000000 +0000
@@ -13,6 +13,8 @@
                                      modedb.o fbcvt.o
 fb-objs                           := $(fb-y)
 
+obj-$(CONFIG_FB_ENHANCED)      += fb_enhanced.o
+
 obj-$(CONFIG_FB_CFB_FILLRECT)  += cfbfillrect.o
 obj-$(CONFIG_FB_CFB_COPYAREA)  += cfbcopyarea.o
 obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
diff -Naur linux-2.6.15.4-original/drivers/video/fb_enhanced.c linux-2.6.15.4/drivers/video/fb_enhanced.c
--- linux-2.6.15.4-original/drivers/video/fb_enhanced.c 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15.4/drivers/video/fb_enhanced.c 2006-02-24 12:56:40.000000000 +0000
@@ -0,0 +1,3086 @@
+/* Framebuffer Enhanced Functionality
+ *
+ * Copyright (C) 2004-2006 Zack T Smith, fbui@comcast.net.
+ *
+ * This code is derived from my FBUI project; see fbui.org.
+ * This code is covered by the GNU Public License and is provided AS-IS
+ * with no warranty or guarantee of any kind in any respect.
+ *
+ * It provides much, in a small space. Specifically:
+ *
+ * (1) a paradigm for more robust use of graphics hardware, involving
+ * + clippability
+ * + alpha blending
+ * + image blit for several useful image types
+ * + line draw
+ * + triangle fill
+ * + robust copyarea
+ *
+ * (2) generic routines to implement these features for
+ * 16/24/32bpp framebuffers, such as VESA.
+ *
+ * Still to do list:
+ * - offscreen drawing
+ * - offscreen to onscreen copy
+ * - overlay support
+ * - reimplementation of my former alpha+foreground drawing
+ * - alpha image blit
+ * - video RAM allocation, deallocation, mapping, unmapping
+ * - PCI-burst fillrect
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/ctype.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+
+
+
+/*-----------------------------------------------------------------------------
+ * Change log:
+ * 05 Sep 2005, fbui@comcast.net: removed 8bpp code.
+ * 05 Sep 2005, fbui@comcast.net: transparent hline speedup.
+ * 10 Sep 2005, fbui@comcast.net: merged point/hline/vline/fillrect.
+ * 12 Sep 2005, fbui@comcast.net: optimized right/leftward copyarea.
+ * 12 Sep 2005, fbui@comcast.net: optimized 32bpp image draw RGB4.
+ * 12 Sep 2005, fbui@comcast.net: optimized 32bpp image draw RGB3.
+ * 12 Sep 2005, fbui@comcast.net: optimized 32bpp image draw RGB2.
+ * 12 Sep 2005, fbui@comcast.net: optimized 32bpp image draw GREY.
+ * 12 Sep 2005, fbui@comcast.net: optimized 32bpp opaque fillrect.
+ * 12 Sep 2005, fbui@comcast.net: optimized 24bpp opaque fillrect.
+ * 13 Sep 2005, fbui@comcast.net: unrolled loops for further speed-up.
+ * 17 Sep 2005, fbui@comcast.net: putimage optimizations using memcpy_toio.
+ * 18 Sep 2005, fbui@comcast.net: copyarea optimizations using memcpy_to/fromio.
+ * 20 Sep 2005, fbui@comcast.net: added 1bpp monochrome putimage.
+ * 22 Sep 2005, fbui@comcast.net: reimplemented fb_enh_line for speedup.
+ * 22 Sep 2005, fbui@comcast.net: speed up of mono putimage by 52%.
+ * 02 Oct 2005, fbui@comcast.net: 24bpp improvements & speedups.
+ * 31 Dec 2005, fbui@comcast.net: created Advanced Functionality from fbui code.
+ *-----------------------------------------------------------------------------
+ */
+
+#ifdef CONFIG_FB_ENHANCED
+
+u32 fb_pixel_from_rgb (struct fb_info *info, u32 value)
+{
+ u32 r,g,b;
+ unsigned long tmp;
+ unsigned long pixel;
+
+ value &= 0xffffff;
+#ifdef CONFIG_FB_UI
+ if (info->mode24)
+  return value;
+ if (info->mode565) {
+  return  ((value >> 8) & 0xf800) |
+   ((value >> 5) & 0x7e0) |
+   ((value >> 3) & 31);
+ }
+ if (info->mode555) {
+  return  ((value >> 9) & 0x7c00) |
+   ((value >> 6) & 0x3e0) |
+   ((value >> 3) & 31);
+ }
+#endif
+
+ tmp = 0xff;
+ b = tmp & value;
+ value >>= 8;
+ g = tmp & value;
+ value >>= 8;
+ r = tmp & value;
+ tmp = 8;
+ r >>= (tmp - info->var.red.length);
+ g >>= (tmp - info->var.green.length);
+ b >>= (tmp - info->var.blue.length);
+ r <<= info->var.red.offset;
+ g <<= info->var.green.offset;
+ b <<= info->var.blue.offset;
+ pixel = r | g | b;
+ return pixel;
+}
+
+
+u32 fb_pixel_to_rgb (struct fb_info *info, u32 value)
+{
+ u32 r,g,b;
+ unsigned char tmp = 8;
+
+#ifdef CONFIG_FB_UI
+ if (info->mode24)
+  return value & 0xffffff;
+ if (info->mode565) {
+  return  ((value << 8) & 0xf80000) |
+   ((value << 5) & 0xfc00) |
+   ((value << 3) & 0xf8);
+ }
+ if (info->mode555) {
+  return  ((value << 9) & 0xf80000) |
+   ((value << 6) & 0xf800) |
+   ((value << 3) & 0xf8);
+ }
+#endif

+ r = value >> info->var.red.offset;
+ g = value >> info->var.green.offset;
+ b = value >> info->var.blue.offset;
+ r &= (1 << info->var.red.length) - 1;
+ b &= (1 << info->var.blue.length) - 1;
+ g &= (1 << info->var.green.length) - 1;
+ r <<= (tmp - info->var.red.length);
+ g <<= (tmp - info->var.green.length);
+ b <<= (tmp - info->var.blue.length);
+ r <<= 16;
+ g <<= 8;
+ return r | g | b;
+}
+
+
+/* This routine combines two pixels based on an 8-bit alpha.
+ */
+inline u32 fb_combine_rgb_pixels (u32 orig_value, u32 value, u32 transp)
+{
+ u32 r,g,b;
+ u32 t;
+ register u32 m = 255;
+ transp &= 255;
+ t = m - transp;
+ r = transp * (m & (orig_value>>16)) + t * (m & (value>>16));
+ g = transp * (m & (orig_value>>8)) + t * (m & (value>>8));
+ b = transp * (m & orig_value) + t * (m & value);
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ value = (r<<16) | (g<<8) | b;
+ return value;
+}
+
+
+#ifdef CONFIG_FB_ENHANCED_32BPP
+/* Optimized function to put opaque image data to a 32bpp display.
+ */
+static void fb_enh_putimage_opaque32 (struct fb_info *info, struct fb_put *p)
+{
+ int i, j;
+        u32 offset;
+ short xres, yres;
+ short width, height;
+ short x0, y0;
+ u8 src_bytes_per_pixel;
+ u32 *dest, *dest_save;
+ u8 *src0;
+ short stride;
+ short x1, y1;
+ short xstart,xend,ystart,yend;
+
+ if (!info || !p)
+  return;
+
+ xstart = p->xstart;
+ xend = p->xend;
+ if (xstart > xend) {
+  short tmp = xstart;
+  xstart = xend;
+  xend = tmp;
+ }
+ ystart = p->ystart;
+ yend = p->yend;
+ if (ystart > yend) {
+  short tmp = ystart;
+  ystart = yend;
+  yend = tmp;
+ }
+ if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height)
+  return;
+ if (xstart < 0)
+  xstart = 0;
+ if (ystart < 0)
+  ystart = 0;
+ if (xend >= p->width)
+  xend = p->width - 1;
+ if (yend >= p->height)
+  yend = p->height - 1;
+
+ src0 = p->pixels;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ width = p->xend - p->xstart + 1;
+ height = p->yend - p->ystart + 1;
+ x0 = p->x0 + xstart;
+ y0 = p->y0 + ystart;
+ x1 = p->x0 + xend;
+ y1 = p->y0 + yend;
+ stride = p->width;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_ALPHA:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_GREY:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_RGB2:
+  src_bytes_per_pixel = 2;
+  break;
+ case FB_IMAGETYPE_RGB3:
+  src_bytes_per_pixel = 3;
+  break;
+ case FB_IMAGETYPE_RGB4:
+  src_bytes_per_pixel = 4;
+  break;
+ default:
+  return;
+ }
+
+ if (!src0 || width <= 0 || height <= 0 ||
+     x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres ||
+     stride <= 0)
+  return;
+ src0 += src_bytes_per_pixel * (stride * ystart + xstart);
+ if (x0 < 0) {
+  x0 = -x0;
+  width -= x0;
+  src0 += x0 * src_bytes_per_pixel;
+  x0 = 0;
+ }
+ if (x1 >= xres) {
+  short diff = x1 - xres + 1;
+  width -= diff;
+  x1 = xres - 1;
+ }
+ if (y0 < 0) {
+  y0 = -y0;
+  height -= y0;
+  src0 += y0 * stride * src_bytes_per_pixel;
+  y0 = 0;
+ }
+ if (y1 >= yres) {
+  short diff = y1 - yres + 1;
+  height -= diff;
+  y1 = yres - 1;
+ }
+
+ if (p->clip_valid) {
+  if (y0 > p->clip_y1 || x0 > p->clip_x1 ||
+      y1 < p->clip_y0 || x1 < p->clip_x0)
+   return;
+  if (x0 < p->clip_x0) {
+   short diff = p->clip_x0 - x0;
+   width -= diff;
+   src0 += diff * src_bytes_per_pixel;
+   x0 = p->clip_x0;
+  }
+  if (x1 > p->clip_x1) {
+   short diff = x1 - p->clip_x1;
+   width -= diff;
+  }
+  if (y0 < p->clip_y0) {
+   short diff = p->clip_y0 - y0;
+   height -= diff;
+   src0 += diff * stride * src_bytes_per_pixel;
+   y0 = p->clip_y0;
+  }
+  if (y1 > p->clip_y1) {
+   short diff = y1 - p->clip_y1;
+   height -= diff;
+  }
+ }
+ /*----------*/
+
+ dest = (u32*) info->screen_base;
+ offset = y0 * (info->fix.line_length>>2) + x0;
+ dest += offset;
+ dest_save = dest;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_RGB4: {
+  u32 *src, *src_save;
+  u32 *buffer;
+#define BUFSIZE32 4096
+
+  /* What is needed is a function to transfer from memory chunks
+   * directly from userspace to memory mapped I/O. Without that,
+   * gotta kmalloc.
+   */
+  buffer = kmalloc(BUFSIZE32, GFP_KERNEL);
+  if (!buffer) {
+   printk(KERN_INFO "fb_enh_putimage_opaque32: no memory\n");
+   return;
+  }
+
+  src = (u32*) src0;
+  src_save = src;
+  
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    u32 pixels = i > (BUFSIZE32/4) ? (BUFSIZE32/4) : i;
+    u32 bytes = pixels << 2;
+
+    /* Data -> L2 cache */
+    if (copy_from_user((u8*)buffer,(u8*)src, bytes))
+     goto dealloc;
+    src += pixels;
+
+    /* L2 cache -> VRAM */
+    memcpy_toio ((u8*)dest, (u8*)buffer, bytes);
+    dest += pixels;
+    i -= pixels;
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += (info->fix.line_length>>2);
+   dest = dest_save;
+   }
+
+dealloc:
+  if (buffer)
+   kfree (buffer);
+    }
+  break;
+
+ case FB_IMAGETYPE_RGB3: {
+  u8 *src = (u8*) src0, *src_save;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   u32 value;
+   i = width;
+   while (i > 0) {
+#if 0
+    if (!(3 & (u32)src)) {
+     if (get_user(value, ((u32*)src)))
+      return;
+     value &= 0xffffff;
+    } else {
+#endif
+     u8 tmp;
+     src += 2;
+     if (get_user(tmp, src)) /* red << 16 */
+      return;
+     value = tmp;
+     value <<= 8;
+     src--;
+     if (get_user(tmp, src)) /* green << 8 */
+      return;
+     value |= tmp;
+     value <<= 8;
+     src--;
+     if (get_user(tmp, src)) /* blue << 0 */
+      return;
+     value |= tmp;
+    //}
+    src += 3;
+
+    fb_writel (value, dest);
+    dest++;
+    i--;
+   }
+   src_save += stride * 3;
+   src = src_save;
+   dest_save += (info->fix.line_length>>2);
+   dest = dest_save;
+  }
+    }
+  break;
+
+ case FB_IMAGETYPE_RGB2: {
+  u16 *src, *src_save;
+  src = (u16*) src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   u32 value,value2;
+   i = width;
+   while (i > 0) {
+    u32 tmp;
+    if (i >= 2) {
+     if (get_user(tmp, ((u32*)src)))
+      return;
+     value = ((tmp << 8) & 0xf80000) |
+      ((tmp << 5) & 0xfc00) |
+      ((tmp << 3) & 0xf8);
+     value2 = ((tmp >> 8) & 0xf80000) |
+      ((tmp >> 11) & 0xfc00) |
+      ((tmp >> 13) & 0xf8);
+     src += 2;
+     fb_writel (value, dest); dest++;
+     fb_writel (value2, dest); dest++;
+     i -= 2;
+    } else {
+     if (get_user(tmp, src))
+      return;
+     value = ((tmp << 8) & 0xf80000) |
+      ((tmp << 5) & 0xfc00) |
+      ((tmp << 3) & 0xf8);
+     src++;
+     fb_writel (value, dest);
+     dest++;
+     i--;
+    }
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += (info->fix.line_length>>2);
+   dest = dest_save;
+  }
+    }
+  break;
+
+ case FB_IMAGETYPE_GREY: {
+  u8 *src, *src_save;
+  src = src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    if (!(3 & (u32)src) && i >= 4) {
+     u32 *s = (u32*) src;
+     u32 value, value2;
+     if (get_user(value, s))
+      return;
+     src += 4;
+     i -= 4;
+
+     value2 = value & 0xff;
+     value2 |= (value2<<8) | (value2<<16);
+     value >>= 8;
+     fb_writel (value2, dest);
+     dest++;
+
+     value2 = value & 0xff;
+     value2 |= (value2<<8) | (value2<<16);
+     value >>= 8;
+     fb_writel (value2, dest);
+     dest++;
+
+     value2 = value & 0xff;
+     value2 |= (value2<<8) | (value2<<16);
+     value >>= 8;
+     fb_writel (value2, dest);
+     dest++;
+
+     value2 = value & 0xff;
+     value2 |= (value2<<8) | (value2<<16);
+     fb_writel (value2, dest);
+     dest++;
+    } else {
+     u32 value;
+     if (get_user(value, src))
+      return;
+     value &= 0xff;
+     value |= (value<<8) | (value<<16);
+     src++;
+     fb_writel (value, dest);
+     dest++;
+     i--;
+    }
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += (info->fix.line_length>>2);
+   dest = dest_save;
+  }
+    }
+  break;
+ }
+
+}
+#endif
+
+
+
+/* Optimized function to put monochrome image. 1's put color, 0's put nothing.
+ */
+static void fb_enh_putimage_mono (struct fb_info *info, struct fb_put *p)
+{
+ int i,j;
+ u32 offset;
+ short xres, yres;
+ short width, height;
+ short x0, y0;
+ u8 *src0;
+ u8 *src, *src_save;
+ short stride;
+ short x1, y1;
+ short xstart, xend, ystart, yend;
+ u8 ix0;
+
+ if (!info || !p)
+  return;
+ if (p->type != FB_IMAGETYPE_MONO)
+  return;
+ xstart = p->xstart;
+ xend = p->xend;
+ if (xstart > xend) {
+  short tmp = xstart;
+  xstart = xend;
+  xend = tmp;
+ }
+ ystart = p->ystart;
+ yend = p->yend;
+ if (ystart > yend) {
+  short tmp = ystart;
+  ystart = yend;
+  yend = tmp;
+ }
+ if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height)
+  return;
+ if (xstart < 0)
+  xstart = 0;
+ if (ystart < 0)
+  ystart = 0;
+ if (xend >= p->width)
+  xend = p->width - 1;
+ if (yend >= p->height)
+  yend = p->height - 1;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ width = xend - xstart + 1;
+ height = yend - ystart + 1;
+ x0 = p->x0 + xstart;
+ y0 = p->y0 + ystart;
+ x1 = p->x0 + xend;
+ y1 = p->y0 + yend;
+ stride = p->width;
+ src0 = p->pixels;
+
+ if (!src0 || width <= 0 || height <= 0 ||
+     x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres ||
+     stride <= 0)
+  return;
+ if (x0 < 0) {
+  x0 = -x0;
+  width -= x0;
+  xstart += x0;
+  x0 = 0;
+ }
+ if (x1 >= xres) {
+  short diff = x1 - xres + 1;
+  width -= diff;
+  x1 = xres - 1;
+  xend -= diff;
+ }
+ if (y0 < 0) {
+  y0 = -y0;
+  height -= y0;
+  ystart += y0;
+  y0 = 0;
+ }
+ if (y1 >= yres) {
+  short diff = y1 - yres + 1;
+  height -= diff;
+  y1 = yres - 1;
+  yend -= diff;
+ }
+
+ if (p->clip_valid) {
+  if (y0 > p->clip_y1 || x0 > p->clip_x1 ||
+      y1 < p->clip_y0 || x1 < p->clip_x0)
+   return;
+  if (x0 < p->clip_x0) {
+   short diff = p->clip_x0 - x0;
+   width -= diff;
+   x0 = p->clip_x0;
+   xstart += diff;
+  }
+  if (x1 > p->clip_x1) {
+   short diff = x1 - p->clip_x1;
+   width -= diff;
+   xend -= diff;
+  }
+  if (y0 < p->clip_y0) {
+   short diff = p->clip_y0 - y0;
+   height -= diff;
+   y0 = p->clip_y0;
+   ystart += diff;
+  }
+  if (y1 > p->clip_y1) {
+   short diff = y1 - p->clip_y1;
+   height -= diff;
+   yend -= diff;
+  }
+ }
+ /*----------*/
+
+ switch ((info->var.bits_per_pixel + 7) >> 3) {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+ case 4: {
+  u32 *dest, *dest_save;
+  dest = (u32*) info->screen_base;
+  offset = y0 * (info->fix.line_length>>2) + x0;
+  dest += offset;
+  dest_save = dest;
+
+  src0 += ((stride+7)>>3) * ystart + (xstart>>3);
+  ix0 = xstart & 7;
+
+  src = src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   u8 k = 1 << ix0;
+   i = width;
+
+   while (i > 0) {
+    if (i >= 32 && !(3 & (u32)src)) {
+     /* 52% faster using this code.
+      */
+     register u32 k2, value, *s=(u32*)src;
+     register u32 *d = dest;
+     register u32 c = p->color;
+     if (get_user(value, s))
+      return;
+     src += 4;
+
+     k2 = k;
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     if (k2 & value) fb_writel (c, d);
+     k2 <<= 1; d++;
+
+     dest = d;
+     k = 1;
+     i-=32;
+    } else {
+     register u8 value;
+     register u32 c = p->color, *s=(u32*)src;
+     if (get_user(value, s))
+      return;
+     src++;
+
+     while (i > 0 && k) {
+      if (k & value)
+       fb_writel (c, dest);
+      dest++;
+      k <<= 1;
+      i--;
+     }
+
+     k = 1;
+    }
+   }
+   src_save += (stride+7)>>3;
+   src = src_save;
+   dest_save += (info->fix.line_length>>2);
+   dest = dest_save;
+  }
+  break;
+   }
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_24BPP
+ case 3: {
+  u8 *dest, *dest_save;
+  dest = info->screen_base;
+  offset = y0 * info->fix.line_length + x0*3;
+  dest += offset;
+  dest_save = dest;
+
+  src0 += ((stride+7)>>3) * ystart + (xstart>>3);
+  ix0 = xstart & 7;
+
+  src = src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   u8 k = 1 << ix0;
+   u32 c = p->color;
+
+   i = width;
+   while (i > 0) {
+    if (i >= 32 && k == 1) {
+     register u32 k2 = k;
+     register u32 value, *s;
+     register u8 *d2 = dest;
+     register u8 r, g;
+     g = c>>8;
+     r = c>>16;
+     s = (u32*) src;
+
+     if (get_user(value, s))
+      return;
+     src += 4;
+
+     while (k2) {
+      if (k2 & value) {
+       fb_writeb (c, d2);
+       fb_writeb (g, (1+d2));
+       fb_writeb (r, (2+d2));
+      }
+      d2 += 3;
+      k2 <<= 1;
+     }
+
+     dest += 96;
+     i -= 32;
+    } else {
+     u8 value;
+     u32 c = p->color;
+     register u8 r, g;
+     g = c>>8;
+     r = c>>16;
+
+     if (get_user(value, src))
+      return;
+     src++;
+
+     while (i > 0 && k) {
+      if (k & value) {
+       fb_writeb (c, dest);
+       fb_writeb (g, (1+dest));
+       fb_writeb (r, (2+dest));
+      }
+      dest += 3;
+      k <<= 1;
+      i--;
+     }
+    }
+    k = 1;
+   }
+
+   src_save += (stride+7)>>3;
+   src = src_save;
+   dest_save += info->fix.line_length;
+   dest = dest_save;
+  }
+  break;
+   }
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_16BPP
+ case 2: {
+  u16 *dest, *dest_save;
+  u16 pixel;
+  dest = (u16*) info->screen_base;
+  offset = y0 * (info->fix.line_length>>1) + x0;
+  dest += offset;
+  dest_save = dest;
+
+  src0 += ((stride+7)>>3) * ystart + (xstart>>3);
+  ix0 = xstart & 7;
+
+  pixel = fb_pixel_from_rgb (info, p->color);
+
+  src = src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   u8 k = 1 << ix0;
+   i = width;
+
+   while (i > 0) {
+    if (i >= 16 && !(3 & (u32)src)) {
+     register u16 k2, value, *s=(u16*)src;
+     register u16 *d = dest;
+     register u16 c = pixel;
+     if (get_user(value, s))
+      return;
+     src += 2;
+     k2 = k;
+
+     if (k2 & value) fb_writew (c, d);
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+1));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+2));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+3));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+4));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+5));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+6));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+7));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+8));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+9));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+10));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+11));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+12));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+13));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+14));
+     k2 <<= 1;
+
+     if (k2 & value) fb_writew (c, (d+15));
+
+     dest += 16;
+     k = 1;
+     i -= 16;
+    } else {
+     u8 value;
+     if (get_user(value, src))
+      return;
+     src++;
+
+     while (i > 0 && k) {
+      if (k & value)
+       fb_writew (pixel, dest);
+      
+      dest++;
+      k <<= 1;
+      i--;
+     }
+
+     k = 1;
+    }
+   }
+   src_save += (stride+7)>>3;
+   src = src_save;
+   dest_save += (info->fix.line_length>>1);
+   dest = dest_save;
+  }
+   }
+#endif
+ }
+}
+
+
+#ifdef CONFIG_FB_ENHANCED_16BPP
+/* Optimized function to put opaque image data to a 16bpp display.
+ */
+static void fb_enh_putimage_opaque16 (struct fb_info *info, struct fb_put *p)
+{
+ int i, j;
+        u32 offset;
+ short xres, yres;
+ short width, height;
+ short x0, y0;
+ u8 *src0;
+ u8 src_bytes_per_pixel;
+ u16 *dest, *dest_save;
+ short stride;
+ short x1, y1;
+ short xstart, xend, ystart, yend;
+
+ if (!info || !p)
+  return;
+
+ xstart = p->xstart;
+ xend = p->xend;
+ if (xstart > xend) {
+  short tmp = xstart;
+  xstart = xend;
+  xend = tmp;
+ }
+ ystart = p->ystart;
+ yend = p->yend;
+ if (ystart > yend) {
+  short tmp = ystart;
+  ystart = yend;
+  yend = tmp;
+ }
+ if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height)
+  return;
+ if (xstart < 0)
+  xstart = 0;
+ if (ystart < 0)
+  ystart = 0;
+ if (xend >= p->width)
+  xend = p->width - 1;
+ if (yend >= p->height)
+  yend = p->height - 1;
+
+ src0 = p->pixels;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ width = p->xend - p->xstart + 1;
+ height = p->yend - p->ystart + 1;
+ x0 = p->x0 + xstart;
+ y0 = p->y0 + ystart;
+ x1 = p->x0 + xend;
+ y1 = p->y0 + yend;
+ stride = p->width;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_ALPHA:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_GREY:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_RGB2:
+  src_bytes_per_pixel = 2;
+  break;
+ case FB_IMAGETYPE_RGB3:
+  src_bytes_per_pixel = 3;
+  break;
+ case FB_IMAGETYPE_RGB4:
+  src_bytes_per_pixel = 4;
+  break;
+ default:
+  return;
+ }
+
+ if (!src0 || width <= 0 || height <= 0 ||
+     x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres ||
+     stride <= 0)
+  return;
+ src0 += src_bytes_per_pixel * (stride * ystart + xstart);
+ if (x0 < 0) {
+  x0 = -x0;
+  width -= x0;
+  src0 += x0 * src_bytes_per_pixel;
+  x0 = 0;
+ }
+ if (x1 >= xres) {
+  short diff = x1 - xres + 1;
+  width -= diff;
+  x1 = xres - 1;
+ }
+ if (y0 < 0) {
+  y0 = -y0;
+  height -= y0;
+  src0 += y0 * stride * src_bytes_per_pixel;
+  y0 = 0;
+ }
+ if (y1 >= yres) {
+  short diff = y1 - yres + 1;
+  height -= diff;
+  y1 = yres - 1;
+ }
+
+ if (p->clip_valid) {
+  if (y0 > p->clip_y1 || x0 > p->clip_x1 ||
+      y1 < p->clip_y0 || x1 < p->clip_x0)
+   return;
+  if (x0 < p->clip_x0) {
+   short diff = p->clip_x0 - x0;
+   width -= diff;
+   src0 += diff * src_bytes_per_pixel;
+   x0 = p->clip_x0;
+  }
+  if (x1 > p->clip_x1) {
+   short diff = x1 - p->clip_x1;
+   width -= diff;
+   /* x1 = p->clip_x1; */
+  }
+  if (y0 < p->clip_y0) {
+   short diff = p->clip_y0 - y0;
+   height -= diff;
+   src0 += diff * stride * src_bytes_per_pixel;
+   y0 = p->clip_y0;
+  }
+  if (y1 > p->clip_y1) {
+   short diff = y1 - p->clip_y1;
+   height -= diff;
+   /* y1 = p->clip_y1; */
+  }
+ }
+ /*----------*/
+
+ dest = (u16*) info->screen_base;
+ offset = y0 * (info->fix.line_length >> 1) + x0;
+ dest += offset;
+ dest_save = dest;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_RGB4: {
+  u32 *src, *src_save;
+  src = (u32*) src0;
+  src_save = src;
+  
+  j = height;
+  while (j-- > 0) {
+   u32 value;
+   i = width;
+   while (i-- > 0) {
+    if (get_user(value, src))
+     return;
+    src++;
+    value = ((value >> 8) & 0xf800) |
+     ((value >> 5) & 0x7e0) |
+     ((value >> 3) & 31);
+    fb_writew (value, dest);
+    dest++;
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += (info->fix.line_length>>1);
+   dest = dest_save;
+  }
+    }
+  break;
+
+ case FB_IMAGETYPE_RGB3: {
+  u8 *src = (u8*) src0, *src_save;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   u32 value;
+   i = width;
+   while (i-- > 0) {
+    if (!(3 & (u32)src)) {
+     if (get_user(value, ((u32*)src)))
+      return;
+     value &= 0xffffff;
+    } else {
+     u8 tmp;
+     src += 2;
+     if (get_user(tmp, src))
+      return;
+     value = tmp;
+     value <<= 8;
+     src--;
+     if (get_user(tmp, src))
+      return;
+     value |= tmp;
+     value <<= 8;
+     src--;
+     if (get_user(tmp, src))
+      return;
+     value |= tmp;
+    }
+    src += 3;
+
+    value = ((value >> 8) & 0xf800) |
+            ((value >> 5) & 0x7e0) |
+            ((value >> 3) & 31);
+    fb_writew (value, dest);
+    dest++;
+   }
+   src_save += stride * 3;
+   src = src_save;
+   dest_save += (info->fix.line_length>>1);
+   dest = dest_save;
+  }
+    }
+  break;
+
+ case FB_IMAGETYPE_RGB2: {
+  u16 *src, *src_save;
+  u8* buffer;
+  src = (u16*) src0;
+  src_save = src;
+
+  /* What is needed is a function to transfer from memory chunks
+   * directly from userspace to memory mapped I/O. Without that,
+   * gotta kmalloc.
+   */
+#define BUFSIZE16 2040
+  buffer = kmalloc(BUFSIZE16, GFP_KERNEL);
+  if (!buffer) {
+   printk(KERN_INFO "fb_enh_putimage_opaque16: no memory\n");
+   return;
+  }
+
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    u32 pixels = i > (BUFSIZE16/2) ? (BUFSIZE16/2) : i;
+    u32 bytes = pixels << 1;
+
+    /* Userspace -> L2 cache */
+    if (copy_from_user((u8*)buffer,(u8*)src, bytes))
+     goto dealloc2;
+    src += pixels;
+
+    /* L2 cache -> VRAM */
+    memcpy_toio ((u8*)dest, (u8*)buffer, bytes);
+    dest += pixels;
+    i -= pixels;
+#if 0
+
+    /* This runs at one half the speed of the
+     * above buffer-based code.
+     */
+    if (!(3 & (u32)src) && i >= 4) {
+     u32 a, b, *s=(u32*)src;
+     if (get_user(a, s))
+      return;
+     s++;
+     if (get_user(b, s))
+      return;
+     src+=4;
+
+     fb_writew (a, dest);
+     dest++;
+     fb_writew (a>>16, dest);
+     dest++;
+     fb_writew (b, dest);
+     dest++;
+     fb_writew (b>>16, dest);
+     dest++;
+     i-=4;
+    } else {
+     u32 tmp;
+     if (get_user(tmp, src))
+      return;
+     tmp &= 0xffff;
+     src++;
+
+     fb_writew (tmp, dest);
+     dest++;
+     i--;
+    }
+#endif
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += (info->fix.line_length>>1);
+   dest = dest_save;
+  }
+
+dealloc2:
+  if (buffer)
+   kfree (buffer);
+    }
+  break;
+
+ case FB_IMAGETYPE_GREY: {
+  u8 *src, *src_save;
+  src = src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    if (!(3 & (u32)src) && i >= 4) {
+     u32 value, value2, *s=(u32*)src;
+     if (get_user(value, s))
+      return;
+     src += 4;
+     i -= 4;
+
+     value2 = value & 0xff;
+     value >>= 8;
+     value2 = ((value2 << 8) & 0xf800) |
+      ((value2 << 3) & 0x7e0) |
+      ((value2 >> 3) & 31);
+     fb_writew (value2, dest);
+     dest++;
+
+     value2 = value & 0xff;
+     value >>= 8;
+     value2 = ((value2 << 8) & 0xf800) |
+      ((value2 << 3) & 0x7e0) |
+      ((value2 >> 3) & 31);
+     fb_writew (value2, dest);
+     dest++;
+
+     value2 = value & 0xff;
+     value >>= 8;
+     value2 = ((value2 << 8) & 0xf800) |
+      ((value2 << 3) & 0x7e0) |
+      ((value2 >> 3) & 31);
+     fb_writew (value2, dest);
+     dest++;
+
+     value2 = value & 0xff;
+     value >>= 8;
+     value2 = ((value2 << 8) & 0xf800) |
+      ((value2 << 3) & 0x7e0) |
+      ((value2 >> 3) & 31);
+     fb_writew (value2, dest);
+     dest++;
+    } else {
+     u32 value;
+     if (get_user(value, src))
+      return;
+     value &= 0xff;
+     value = ((value << 8) & 0xf800) |
+      ((value << 3) & 0x7e0) |
+      ((value >> 3) & 31);
+     src++;
+
+     fb_writew (value, dest);
+     dest++;
+     i--;
+    }
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += (info->fix.line_length>>1);
+   dest = dest_save;
+  }
+    }
+  break;
+ }
+}
+#endif
+
+
+#ifdef CONFIG_FB_ENHANCED_24BPP
+/* Optimized function to put opaque image data to a 24bpp display.
+ */
+static void fb_enh_putimage_opaque24 (struct fb_info *info, struct fb_put *p)
+{
+ int i, j;
+        u32 offset;
+ short xres, yres;
+ short width, height;
+ short x0, y0;
+ u8 *src0;
+ u8 src_bytes_per_pixel;
+ u8 *dest, *dest_save;
+ short stride;
+ short x1, y1;
+ short xstart, xend, ystart, yend;
+
+ if (!info || !p)
+  return;
+
+ xstart = p->xstart;
+ xend = p->xend;
+ if (xstart > xend) {
+  short tmp = xstart;
+  xstart = xend;
+  xend = tmp;
+ }
+ ystart = p->ystart;
+ yend = p->yend;
+ if (ystart > yend) {
+  short tmp = ystart;
+  ystart = yend;
+  yend = tmp;
+ }
+ if (xend < 0 || yend < 0 || xstart >= p->width || ystart >= p->height)
+  return;
+ if (xstart < 0)
+  xstart = 0;
+ if (ystart < 0)
+  ystart = 0;
+ if (xend >= p->width)
+  xend = p->width - 1;
+ if (yend >= p->height)
+  yend = p->height - 1;
+
+ src0 = p->pixels;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ width = p->xend - p->xstart + 1;
+ height = p->yend - p->ystart + 1;
+ x0 = p->x0 + xstart;
+ y0 = p->y0 + ystart;
+ x1 = p->x0 + xend;
+ y1 = p->y0 + yend;
+ stride = p->width;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_ALPHA:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_GREY:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_RGB2:
+  src_bytes_per_pixel = 2;
+  break;
+ case FB_IMAGETYPE_RGB3:
+  src_bytes_per_pixel = 3;
+  break;
+ case FB_IMAGETYPE_RGB4:
+  src_bytes_per_pixel = 4;
+  break;
+ default:
+  return;
+ }
+
+ if (!src0 || width <= 0 || height <= 0 ||
+     x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres ||
+     stride <= 0)
+  return;
+ src0 += src_bytes_per_pixel * (stride * ystart + xstart);
+ if (x0 < 0) {
+  x0 = -x0;
+  width -= x0;
+  src0 += x0 * src_bytes_per_pixel;
+  x0 = 0;
+ }
+ if (x1 >= xres) {
+  short diff = x1 - xres + 1;
+  width -= diff;
+  x1 = xres - 1;
+ }
+ if (y0 < 0) {
+  y0 = -y0;
+  height -= y0;
+  src0 += y0 * stride * src_bytes_per_pixel;
+  y0 = 0;
+ }
+ if (y1 >= yres) {
+  short diff = y1 - yres + 1;
+  height -= diff;
+  y1 = yres - 1;
+ }
+
+ if (p->clip_valid) {
+  if (y0 > p->clip_y1 || x0 > p->clip_x1 ||
+      y1 < p->clip_y0 || x1 < p->clip_x0)
+   return;
+  if (x0 < p->clip_x0) {
+   short diff = p->clip_x0 - x0;
+   width -= diff;
+   src0 += diff * src_bytes_per_pixel;
+   x0 = p->clip_x0;
+  }
+  if (x1 > p->clip_x1) {
+   short diff = x1 - p->clip_x1;
+   width -= diff;
+   /* x1 = p->clip_x1; */
+  }
+  if (y0 < p->clip_y0) {
+   short diff = p->clip_y0 - y0;
+   height -= diff;
+   src0 += diff * stride * src_bytes_per_pixel;
+   y0 = p->clip_y0;
+  }
+  if (y1 > p->clip_y1) {
+   short diff = y1 - p->clip_y1;
+   height -= diff;
+   /* y1 = p->clip_y1; */
+  }
+ }
+ /*----------*/
+
+ dest = (u8*) info->screen_base;
+ offset = y0 * info->fix.line_length + 3 * x0;
+ dest += offset;
+ dest_save = dest;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_RGB4: {
+  u32 *src, *src_save;
+  src = (u32*) src0;
+  src_save = src;
+  
+  j = height;
+  while (j-- > 0) {
+   u32 value;
+   i = width;
+   while (i-- > 0) {
+    if (get_user(value, src))
+     return;
+    src++;
+
+    fb_writeb (value, dest); dest++;
+    fb_writeb (value>>8, dest); dest++;
+    fb_writeb (value>>16, dest); dest++;
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += info->fix.line_length;
+   dest = dest_save;
+   }
+    }
+  break;
+
+ case FB_IMAGETYPE_RGB3: {
+  u8 *src = (u8*) src0, *src_save;
+  u8 *buffer;
+
+  /* What is needed is a function to transfer from memory chunks
+   * directly from userspace to memory mapped I/O. Without that,
+   * gotta kmalloc.
+   */
+#define BUFSIZE24 3072
+  buffer = kmalloc(BUFSIZE24, GFP_KERNEL);
+  if (!buffer) {
+   printk(KERN_INFO "fb_enh_putimage_opaque16: no memory\n");
+   return;
+  }
+
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    u32 pixels = i > (BUFSIZE24/3) ? (BUFSIZE24/3) : i;
+    u32 bytes = pixels * 3;
+
+    /* Userspace -> L2 cache */
+    if (copy_from_user((u8*)buffer,(u8*)src, bytes))
+     goto dealloc3;
+    src += pixels;
+
+    /* L2 cache -> VRAM */
+    memcpy_toio ((u8*)dest, (u8*)buffer, bytes);
+    dest += pixels;
+    i -= pixels;
+
+#if 0
+  j = height;
+  while (j-- > 0) {
+   u32 value;
+   i = width;
+   while (i-- > 0) {
+    u8 a,b,c;
+
+    if (get_user(a, src))
+     return;
+    src++;
+    if (get_user(b, src))
+     return;
+    src++;
+    if (get_user(c, src))
+     return;
+    src++;
+
+    fb_writeb (a, dest); dest++;
+    fb_writeb (b, dest); dest++;
+    fb_writeb (c, dest); dest++;
+#endif
+   }
+   src_save += stride * 3;
+   src = src_save;
+   dest_save += info->fix.line_length;
+   dest = dest_save;
+  }
+dealloc3:
+  if (buffer)
+   kfree (buffer);
+    }
+  break;
+
+ case FB_IMAGETYPE_RGB2: {
+  u16 *src, *src_save;
+  src = (u16*) src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    u32 value;
+    u32 tmp;
+
+    if (get_user(tmp, src))
+     return;
+    src++;
+
+    value = ((tmp << 8) & 0xf80000) |
+     ((tmp << 5) & 0xfc00) |
+     ((tmp << 3) & 0xf8);
+    fb_writeb (value, dest); dest++; value >>= 8;
+    fb_writeb (value, dest); dest++; value >>= 8;
+    fb_writeb (value, dest); dest++;
+    i--;
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += info->fix.line_length;
+   dest = dest_save;
+   }
+    }
+  break;
+
+ case FB_IMAGETYPE_GREY: {
+  u8 *src, *src_save;
+  src = src0;
+  src_save = src;
+
+  j = height;
+  while (j-- > 0) {
+   i = width;
+   while (i > 0) {
+    u8 value;
+    if (get_user(value, src))
+     return;
+    src++;
+
+    fb_writeb (value, dest); dest++;
+    fb_writeb (value, dest); dest++;
+    fb_writeb (value, dest); dest++;
+    i--;
+   }
+   src_save += stride;
+   src = src_save;
+   dest_save += info->fix.line_length;
+   dest = dest_save;
+   }
+    }
+  break;
+ }
+}
+#endif
+
+
+/* generic_putimage
+ *
+ * Supports source images of:
+ * . native depth
+ * . 24 bits per pixel
+ * . 32 bits per pixel with transparency
+ * . 8 bits grey
+ *
+ * Re transparency:
+ * 0 is opaque, 255 is 100% transparent.
+ */
+
+static void generic_putimage (struct fb_info *info, struct fb_put *p)
+{
+ int i, j;
+        int offset;
+        u16 bytes_per_pixel;
+        unsigned char *dest;
+        unsigned char *dest_save;
+ unsigned char *src, *src_save;
+ short xres, yres;
+ short width, height;
+ short x0, y0;
+ short x1, y1;
+ u32 src_bytes_per_pixel;
+ short stride;
+
+ if (!info || !p)
+  return;
+ if (p->type == FB_IMAGETYPE_MONO)
+  return;
+ if (p->xend < 0 || p->yend < 0 || p->xstart >= p->width || p->ystart >= p->height)
+  return;
+ if (p->xstart > p->xend) {
+  short tmp = p->xstart;
+  p->xstart = p->xend;
+  p->xend = tmp;
+ }
+ if (p->ystart > p->yend) {
+  short tmp = p->ystart;
+  p->ystart = p->yend;
+  p->yend = tmp;
+ }
+ if (p->xstart < 0)
+  p->xstart = 0;
+ if (p->ystart < 0)
+  p->ystart = 0;
+ if (p->xend >= p->width)
+  p->xend = p->width - 1;
+ if (p->yend >= p->height)
+  p->yend = p->height - 1;
+ src = p->pixels;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ width = p->xend - p->xstart + 1;
+ height = p->yend - p->ystart + 1;
+ x0 = p->x0;
+ y0 = p->y0;
+ x1 = p->x1;
+ y1 = p->y1;
+ stride = p->width;
+
+ switch (p->type) {
+ case FB_IMAGETYPE_GREY:
+  src_bytes_per_pixel = 1;
+  break;
+ case FB_IMAGETYPE_RGB2:
+  src_bytes_per_pixel = 2;
+  break;
+ case FB_IMAGETYPE_RGB3:
+  src_bytes_per_pixel = 3;
+  break;
+ case FB_IMAGETYPE_RGB4:
+ case FB_IMAGETYPE_RGBA:
+  src_bytes_per_pixel = 4;
+  break;
+ default:
+  return;
+ }
+
+ if (!src || width <= 0 || height <= 0 ||
+     x1 < 0 || y1 < 0 || x0 >= xres || y0 >= yres ||
+     stride <= 0)
+  return;
+ src += src_bytes_per_pixel * (p->width * p->ystart + p->xstart);
+ if (x0 < 0) {
+  x0 = -x0;
+  width -= x0;
+  src += x0 * src_bytes_per_pixel;
+  x0 = 0;
+ }
+ if (x1 >= xres) {
+  short diff = x1 - xres + 1;
+  width -= diff;
+  x1 = xres - 1;
+ }
+ if (y0 < 0) {
+  y0 = -y0;
+  height -= y0;
+  src += y0 * stride * src_bytes_per_pixel;
+  y0 = 0;
+ }
+ if (y1 >= yres) {
+  short diff = y1 - yres + 1;
+  height -= diff;
+  y1 = yres - 1;
+ }
+
+ if (p->clip_valid) {
+  if (y0 > p->clip_y1 || x0 > p->clip_x1 ||
+      y1 < p->clip_y0 || x1 < p->clip_x0)
+   return;
+  if (x0 < p->clip_x0) {
+   short diff = p->clip_x0 - x0;
+   width -= diff;
+   src += diff * src_bytes_per_pixel;
+   x0 = p->clip_x0;
+  }
+  if (x1 > p->clip_x1) {
+   short diff = x1 - p->clip_x1;
+   width -= diff;
+  }
+  if (y0 < p->clip_y0) {
+   short diff = p->clip_y0 - y0;
+   height -= diff;
+   src += diff * stride * src_bytes_per_pixel;
+   y0 = p->clip_y0;
+  }
+  if (y1 > p->clip_y1) {
+   short diff = y1 - p->clip_y1;
+   height -= diff;
+  }
+ }
+ /*----------*/
+
+        bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+        dest = info->screen_base;
+ offset = (y0 + p->ystart) * info->fix.line_length +
+   (x0 + p->xstart) * bytes_per_pixel;
+        dest += offset;
+ dest_save = dest;
+ src_save = src;

+ j = height;
+ while (j-- > 0) {
+  i = width;
+  while (i-- > 0) {
+   u32 value=0;
+   u32 transp=0;
+   u32 orig_value;
+
+   switch (p->location) {
+   case FB_LOCATION_KERNEL:
+    switch (src_bytes_per_pixel) {
+    case 1:
+     value = *src++;
+     break;
+    case 2:
+     value = *(unsigned short*) src;
+     src += 2;
+     break;
+    case 3: {
+     value = src[2];
+     value <<= 8;
+     value |= src[1];
+     value <<= 8;
+     value |= *src++;
+     src += 2;
+     break;
+     }
+    case 4:
+     value = *(unsigned long*) src;
+     src += 4;
+    }
+    break;
+
+   case FB_LOCATION_USER:
+    switch (src_bytes_per_pixel) {
+    case 1: {
+     u8 tmp=0;
+     if (get_user(tmp, src))
+      return;
+     src++;
+     value = tmp;
+     break;
+     }
+    case 2: {
+     u16 tmp=0, *s=(u16*)src;
+     if (get_user(tmp, s))
+      return;
+     src+=2;
+     value = tmp;
+     break;
+     }
+    case 3: {
+     u8 tmp;
+     src += 2;
+     if (get_user(tmp, src))
+      return;
+     value = tmp;
+     value <<= 8;
+     src--;
+     if (get_user(tmp, src))
+      return;
+     value |= tmp;
+     value <<= 8;
+     src--;
+     if (get_user(tmp, src))
+      return;
+     value |= tmp;
+     src += 3;
+     break;
+     }
+    case 4:  {
+     u32 *s=(u32*)src;
+     if (get_user(value, s))
+      return;
+     src+=4;
+     break;
+     }
+    }
+    break;
+
+   case FB_LOCATION_VRAM:
+    value = 0;
+    break;
+
+   default:
+    return;
+   }
+
+   switch (p->type) {
+   case FB_IMAGETYPE_RGBA:
+    transp = value >> 24;
+    break;
+   case FB_IMAGETYPE_RGB2: {
+     u32 r,g,b;
+     r = (value >> 11) & 31;
+     g = (value >> 5) & 63;
+     b = value & 31;
+     r <<= 19;
+     g <<= 10;
+     b <<= 3;
+     value = r | g | b;
+    }
+    break;
+   case FB_IMAGETYPE_GREY:
+    value |= (value<<8) | (value<<16);
+   }
+
+   if (transp > 0 && transp < 255) {
+    orig_value = 0;
+    switch (bytes_per_pixel) {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+    case 4:
+     orig_value = fb_readl (dest);
+     break;
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_24BPP
+    case 3: {
+     u32 tmp;
+     orig_value = 0xff & fb_readb (dest);
+     dest++;
+     tmp = 0xff & fb_readb (dest);
+     dest++;
+     tmp <<= 8;
+     orig_value |= tmp;
+     tmp = 0xff & fb_readb (dest);
+     tmp <<= 16;
+     orig_value |= tmp;
+     dest -= 2;
+     break;
+    }
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_16BPP
+    case 2:
+     orig_value = fb_readw (dest);
+     break;
+#endif
+
+    }
+
+    orig_value = fb_pixel_to_rgb (info, orig_value);
+    value = fb_combine_rgb_pixels (orig_value, value, transp);
+   }
+
+   if (transp == 255)
+    dest += bytes_per_pixel;
+   else {
+    value = fb_pixel_from_rgb (info, value);
+
+    switch (bytes_per_pixel)
+    {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+    case 4:
+     fb_writel (value, dest);
+     dest += 4;
+     break;
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_24BPP
+    case 3:
+     fb_writeb (value, dest); dest++; value >>= 8;
+     fb_writeb (value, dest); dest++; value >>= 8;
+     fb_writeb (value, dest); dest++;
+     break;
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_16BPP
+    case 2:
+     fb_writew (value, dest);
+     dest += 2;
+     break;
+#endif
+
+    default:
+     break;
+    }
+   }
+  }
+  src_save += stride * src_bytes_per_pixel;
+  src = src_save;
+  dest_save += info->fix.line_length;
+  dest = dest_save;
+ }
+}
+
+static void fb_enh_putimage (struct fb_info *info, struct fb_put *p)
+{
+ u8 bytes_per_pixel;
+
+ if (!info || !p)
+  return;
+ /*----------*/
+
+ bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+
+ if (p->type == FB_IMAGETYPE_MONO) {
+  fb_enh_putimage_mono (info, p);
+  return;
+ }
+ else
+ if (p->type != FB_IMAGETYPE_RGBA) {
+  if (bytes_per_pixel==2 &&
+      info->var.red.length == 5 &&
+      info->var.green.length == 6 &&
+      info->var.blue.length == 5) {
+#ifdef CONFIG_FB_ENHANCED_16BPP
+   fb_enh_putimage_opaque16 (info, p);
+   return;
+#endif
+  } else
+  if (info->var.red.length == 8 &&
+      info->var.green.length == 8 &&
+      info->var.blue.length == 8) {
+   if (bytes_per_pixel==4) {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+    fb_enh_putimage_opaque32 (info, p);
+#endif
+   } else {
+#ifdef CONFIG_FB_ENHANCED_24BPP
+    fb_enh_putimage_opaque24 (info, p);
+#endif
+   }
+   return;
+  }
+ }
+
+ generic_putimage (info, p);
+}

+
+static void generic_fillrect (struct fb_info *info, struct fb_draw *p)
+{
+ u32 pixel, bytes_per_pixel, offset;
+ short xres, yres;
+ unsigned char *ptr, *ptr_save;
+ u32 transp;
+ short x0,x1,y0,y1;
+ int i, j;
+
+ if (!info || !p)
+  return;
+ x0 = p->x0;
+ y0 = p->y0;
+ x1 = p->x1;
+ y1 = p->y1;
+ transp = p->color >> 24;
+ if (transp == 255)
+  return;
+ if (x0 > x1) {
+  short tmp = x0;
+  x0 = x1;
+  x1 = tmp;
+ }
+ if (y0 > y1) {
+  short tmp = y0;
+  y0 = y1;
+  y1 = tmp;
+ }
+ if (y1 < 0 || x1 < 0)
+  return;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ if (x0 >= xres || y0 >= yres)
+  return;
+ if (x0 < 0)
+  x0 = 0;
+ if (x1 >= xres)
+  x1 = xres-1;
+ if (y0 < 0)
+  y0 = 0;
+ if (y1 >= yres)
+  y1 = yres-1;
+ if (p->clip_valid) {
+  if (y1 < p->clip_y0 || y0 > p->clip_y1 ||
+      x1 < p->clip_x0 || x0 > p->clip_x1)
+   return;
+
+  if (x0 < p->clip_x0)
+   x0 = p->clip_x0;
+  if (x1 > p->clip_x1)
+   x1 = p->clip_x1;
+  if (y0 < p->clip_y0)
+   y0 = p->clip_y0;
+  if (y1 > p->clip_y1)
+   y1 = p->clip_y1;
+ }
+ /*----------*/
+
+ bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+ offset = y0 * info->fix.line_length + x0 * bytes_per_pixel;
+ ptr = ((unsigned char*)info->screen_base);
+ ptr += offset;
+ ptr_save = ptr;
+ pixel = fb_pixel_from_rgb (info, p->color);
+
+ j = y1 - y0 + 1;
+ while (j--) {
+  ptr = ptr_save;
+
+  i = x1 - x0 + 1;
+
+  while (i--) {
+   if (transp) {
+    u32 orig_value = 0;
+    u32 value = p->color;
+
+    switch (bytes_per_pixel) {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+    case 4:
+     orig_value = fb_readl (ptr);
+     break;
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_24BPP
+    case 3: {
+     u32 tmp;
+     orig_value = 0xff & fb_readb (ptr);
+     ptr++;
+     tmp = 0xff & fb_readb (ptr);
+     ptr++;
+     tmp <<= 8;
+     orig_value |= tmp;
+     tmp = 0xff & fb_readb (ptr);
+     tmp <<= 16;
+     orig_value |= tmp;
+     ptr -= 2;
+     break;
+    }
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_16BPP
+    case 2:
+     orig_value = fb_readw (ptr);
+     break;
+#endif
+
+    }
+
+    orig_value = fb_pixel_to_rgb (info, orig_value);
+    value = fb_combine_rgb_pixels (orig_value, value, transp);
+    pixel = fb_pixel_from_rgb (info, value);
+   }
+
+   switch (bytes_per_pixel) {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+   case 4:
+    fb_writel (pixel, ptr);
+    ptr += 4;
+    break;
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_24BPP
+   case 3: {
+    register u32 c = pixel;
+    fb_writeb (c, ptr); ptr++; c >>= 8;
+    fb_writeb (c, ptr); ptr++; c >>= 8;
+    fb_writeb (c, ptr); ptr++;
+    break;
+   }
+#endif
+
+#ifdef CONFIG_FB_ENHANCED_16BPP
+   case 2:
+    fb_writew (pixel, ptr);
+    ptr += 2;
+    break;
+#endif
+   }/*switch*/
+  }
+
+  ptr_save += info->fix.line_length;
+ }
+}
+
+static void fb_enh_fillrect (struct fb_info *info, struct fb_draw *p)
+{
+ u32 offset;
+ short xres, yres;
+ short x0,x1,y0,y1;
+ int i, j;
+
+ if (!info || !p)
+  return;
+ x0 = p->x0;
+ y0 = p->y0;
+ x1 = p->x1;
+ y1 = p->y1;
+ if (p->color >> 24) {
+  generic_fillrect (info, p);
+  return;
+ }
+
+ if (x0 > x1) {
+  short tmp = x0;
+  x0 = x1;
+  x1 = tmp;
+ }
+ if (y0 > y1) {
+  short tmp = y0;
+  y0 = y1;
+  y1 = tmp;
+ }
+
+ if (y1 < 0 || x1 < 0)
+  return;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ if (x0 >= xres || y0 >= yres)
+  return;
+ if (x0 < 0)
+  x0 = 0;
+ if (x1 >= xres)
+  x1 = xres-1;
+ if (y0 < 0)
+  y0 = 0;
+ if (y1 >= yres)
+  y1 = yres-1;
+ if (p->clip_valid) {
+  if (y1 < p->clip_y0 || y0 > p->clip_y1 ||
+      x1 < p->clip_x0 || x0 > p->clip_x1)
+   return;
+
+  if (x0 < p->clip_x0)
+   x0 = p->clip_x0;
+  if (x1 > p->clip_x1)
+   x1 = p->clip_x1;
+  if (y0 < p->clip_y0)
+   y0 = p->clip_y0;
+  if (y1 > p->clip_y1)
+   y1 = p->clip_y1;
+ }
+ /*----------*/
+
+ switch (((info->var.bits_per_pixel + 7) >> 3)) {
+ case 4: {
+#ifdef CONFIG_FB_ENHANCED_32BPP
+  u32 *ptr, *ptr_save;
+  short w = x1 - x0 + 1;
+
+  ptr = ((u32*)info->screen_base);
+  offset = y0 * (info->fix.line_length>>2) + x0;
+  ptr += offset;
+  ptr_save = ptr;
+
+  j = y1 - y0 + 1;
+  while (j-- > 0) {
+   register u32 px = p->color;
+   i = w;
+
+   while (i > 0) {
+    if (i >= 8) {
+     register u32 *p = ptr;
+     fb_writel (px, p);
+     fb_writel (px, (p+1));
+     fb_writel (px, (p+2));
+     fb_writel (px, (p+3));
+     fb_writel (px, (p+4));
+     fb_writel (px, (p+5));
+     fb_writel (px, (p+6));
+     fb_writel (px, (p+7));
+     ptr += 8;
+     i -= 8;
+    } else {
+     fb_writel (px, ptr); ptr++;
+     i--;
+    }
+   }
+
+   ptr_save += info->fix.line_length>>2;
+   ptr = ptr_save;
+  }
+#endif
+  }
+  break;
+
+ case 3: {
+#ifdef CONFIG_FB_ENHANCED_24BPP
+  u8 *ptr, *ptr_save;
+  u8 r,g,b;
+  short w = x1 - x0 + 1;
+  union {
+   u32 l[3];
+   u8 c[12];
+  } stripe;
+  r = p->color >> 16;
+  g = p->color >> 8;
+  b = p->color;
+  for (i=0; i<=9; i+=3) {
+   stripe.c[i] = b;
+   stripe.c[i+1] = g;
+   stripe.c[i+2] = r;
+  }
+
+  ptr = ((u8*)info->screen_base);
+  offset = y0 * info->fix.line_length + x0*3;
+  ptr += offset;
+  ptr_save = ptr;
+
+  j = y1 - y0 + 1;
+  while (j-- > 0) {
+   i = w;
+
+   while (i > 0) {
+    if (i>=12 && !(3 & (u32)ptr)) {
+     register u32 a,b,c;
+     register u32 *p = (u32*)ptr;
+     a = stripe.l[0];
+     b = stripe.l[1];
+     c = stripe.l[2];
+     fb_writel (a, p); p++;
+     fb_writel (b, p); p++;
+     fb_writel (c, p); p++;
+     fb_writel (a, p); p++;
+     fb_writel (b, p); p++;
+     fb_writel (c, p); p++;
+     fb_writel (a, p); p++;
+     fb_writel (b, p); p++;
+     fb_writel (c, p); p++;
+     ptr += 36;
+     i -= 12;
+    } else {
+     fb_writeb (b, ptr); ptr++;
+     fb_writeb (g, ptr); ptr++;
+     fb_writeb (r, ptr); ptr++;
+     i--;
+    }
+   }
+
+   ptr_save += info->fix.line_length;
+   ptr = ptr_save;
+   }
+#endif
+  }
+  break;
+
+ case 2: {
+#ifdef CONFIG_FB_ENHANCED_16BPP
+  u16 *ptr, *ptr_save;
+  u16 pix;
+  u32 pix2;
+  short w = x1 - x0 + 1;
+
+  pix = fb_pixel_from_rgb (info,p->color);
+  pix2 = pix;
+  pix2 <<= 16;
+  pix2 |= pix;
+  ptr = ((u16*)info->screen_base);
+  offset = y0 * (info->fix.line_length >> 1) + x0;
+  ptr += offset;
+  ptr_save = ptr;
+
+  j = y1 - y0 + 1;
+  while (j-- > 0) {
+   i = w;
+
+   while (i > 0) {
+    if (!(3 & (u32)ptr) && i >= 8) {
+     register u32 *p = (u32*) ptr;
+     fb_writel (pix2, p);
+     fb_writel (pix2, (1+p));
+     fb_writel (pix2, (2+p));
+     fb_writel (pix2, (3+p));
+     ptr += 8;
+     i -= 8;
+    } else {
+     fb_writew (pix, ptr);
+     ptr++;
+     i--;
+    }
+   }
+
+   ptr_save += info->fix.line_length>>1;
+   ptr = ptr_save;
+  }
+#endif
+  }
+  break;
+ }
+}
+
+
+static void fb_enh_copy_within (unsigned char *dest, unsigned char *src, u32 n)
+{
+ int dif = dest < src ? src - dest : dest - src;
+ while (n) {
+  if (dif >= 4 && n >= 16 && !(3 & (u32)src) && !(3 & (u32)dest)){
+   /* This is most effective on pre-Pentium CPUs.
+    * and maybe could be shrunk down for >= Pentium.
+    */
+   fb_writel (fb_readl (src), dest); src+=4; dest+=4;
+   fb_writel (fb_readl (src), dest); src+=4; dest+=4;
+   fb_writel (fb_readl (src), dest); src+=4; dest+=4;
+   fb_writel (fb_readl (src), dest); src+=4; dest+=4;
+   n -= 16;
+  } else {
+   fb_writeb (fb_readb (src), dest); src++; dest++;
+   n--;
+  }
+ }
+}
+
+
+/* Robust copyarea, supporting 16/24/32bpp, shifting up/down/left/right.
+ */
+static void fb_enh_copyarea (struct fb_info *info, struct fb_draw *p)
+{
+        u32 bytes_per_pixel, offset, rasterlen;
+        unsigned char *src;
+        unsigned char *dest;
+ short w, h;
+ short xres, yres;
+ u32 stride;
+ short x0,y0,x1,y1,x2,y2;
+
+ if (!info || !p)
+  return;
+ x0 = p->x0;
+ y0 = p->y0;
+ x1 = p->x1;
+ y1 = p->y1;
+ x2 = p->x2;
+ y2 = p->y2;
+ if (x0 > x1) {
+  short tmp = x0;
+  x0 = x1;
+  x1 = tmp;
+ }
+ if (y0 > y1) {
+  short tmp = y0;
+  y0 = y1;
+  y1 = tmp;
+ }
+ if (x1 < 0 || y1 < 0)
+  return;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ if (x0 >= xres || y0 >= yres || x2 >= xres || y2 >= yres)
+  return;
+ w = x1 - x0 + 1;
+ h = y1 - y0 + 1;
+ if (w<=0 || h<=0)
+  return;
+ if (x2+w-1 < 0 || y2+h-1 < 0)
+  return;
+ if (x0 < 0) {
+  x2 -= x0;
+  w += x0;
+  x0=0;
+ }
+ if (y0 < 0) {
+  y2 -= y0;
+  h += y0;
+  y0=0;
+ }
+ if (x1 >= xres) {
+  short diff = x1 - xres + 1;
+  w -= diff;
+  x1 = xres-1;
+ }
+ if (y1 >= yres) {
+  short diff = y1 - yres + 1;
+  h -= diff;
+  y1 = yres-1;
+ }
+ if (x2+w-1 >= xres) {
+  short diff = x2 + w - xres;
+  w -= diff;
+ }
+ if (y2+h-1 >= yres) {
+  short diff = y2+h - yres;
+  h -= diff;
+ }
+ if (p->clip_valid) {
+  if (x1 < p->clip_x0 || y1 < p->clip_y0)
+   return;
+  if (x2+w-1 < p->clip_x0 || y2+h-1 < p->clip_y0)
+   return;
+  if (x0 > p->clip_x1 || y0 > p->clip_y1)
+   return;
+  if (x2 > p->clip_x1 || y2 > p->clip_y1)
+   return;
+  if (x0 < p->clip_x0) {
+   short diff = p->clip_x0 - x0;
+   x2 += diff;
+   w -= diff;
+   x0 = p->clip_x0;
+  }
+  if (y0 < p->clip_y0) {
+   short diff = p->clip_y0 - y0;
+   y2 += diff;
+   h -= diff;
+   y0 = p->clip_y0;
+  }
+  if (x1 > p->clip_x1) {
+   short diff = x1 - p->clip_x1;
+   w -= diff;
+   x1 = p->clip_x1;
+  }
+  if (y1 > p->clip_y1) {
+   short diff = y1 - p->clip_y1;
+   h -= diff;
+   y1 = p->clip_y1;
+  }
+  if (x2 < p->clip_x0) {
+   short diff = p->clip_x0 - x2;
+   x0 += diff;
+   w -= diff;
+   x2 = p->clip_x0;
+  }
+  if (y2 < p->clip_y0) {
+   short diff = p->clip_y0 - y2;
+   y0 += diff;
+   h -= diff;
+   y2 = p->clip_y0;
+  }
+  if (x2+w-1 > p->clip_x1) {
+   short diff = x2+w-1 - p->clip_x1;
+   w -= diff;
+  }
+  if (y2+h-1 > p->clip_y1) {
+   short diff = y2+h-1 - p->clip_y1;
+   h -= diff;
+  }
+ }
+ /*----------*/
+
+/*printk (KERN_INFO "copyarea: w=%d h=%d\n", w, h);*/
+        bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+        rasterlen = info->fix.line_length;
+        src = dest = info->screen_base;
+ offset = y0 * rasterlen + x0 * bytes_per_pixel;
+        src += offset;
+ offset = y2 * rasterlen + x2 * bytes_per_pixel;
+ dest += offset;
+ stride = w * bytes_per_pixel;
+
+ if (y2 < y0) {
+  int j=0;
+
+#define BUFSIZECOPY 4096
+  u8 *buffer = NULL;
+  buffer = kmalloc (BUFSIZECOPY, GFP_KERNEL);
+
+  /* moving upward */
+  while (j < h) {
+   if (!buffer)
+    fb_enh_copy_within (dest,src,stride);
+   else {
+    u8 *d = dest;
+    u8 *s = src;
+    long st = stride;
+    while (st > 0) {
+     u32 len = st > BUFSIZECOPY ?
+      BUFSIZECOPY : st;
+     memcpy_fromio (buffer, s, len);
+     memcpy_toio (d, buffer, len);
+     st -= len;
+     s += len;
+     d += len;
+    }
+   }
+   dest += rasterlen;
+   src += rasterlen;
+   j++;
+  }
+  if (buffer)
+   kfree (buffer);
+ } else
+ if (y2 > y0) {
+  u8 *buffer = NULL;
+  int j=h-1;
+
+  buffer = kmalloc (BUFSIZECOPY, GFP_KERNEL);
+
+  /* moving downward */
+  dest += rasterlen * h;
+  src += rasterlen * h;
+  while (j >= 0) {
+   dest -= rasterlen;
+   src -= rasterlen;
+   if (!buffer)
+    fb_enh_copy_within (dest,src,stride);
+   else {
+    u8 *d = dest;
+    u8 *s = src;
+    long st = stride;
+    while (st > 0) {
+     u32 len = st > BUFSIZECOPY ?
+      BUFSIZECOPY : st;
+     memcpy_fromio (buffer, s, len);
+     memcpy_toio (d, buffer, len);
+     st -= len;
+     s += len;
+     d += len;
+    }
+   }
+   j--;
+  }
+  kfree (buffer);
+ } else {
+  int j=0;
+  /* y equal */
+
+  u8 *buffer = NULL;
+  buffer = kmalloc (BUFSIZECOPY, GFP_KERNEL);
+
+  /* moving upward */
+  while (j < h) {
+   if (!buffer)
+    fb_enh_copy_within (dest,src,stride);
+   else {
+    u8 *d = dest;
+    u8 *s = src;
+    long st = stride;
+    while (st > 0) {
+     u32 len = st > BUFSIZECOPY ?
+      BUFSIZECOPY : st;
+     memcpy_fromio (buffer, s, len);
+     memcpy_toio (d, buffer, len);
+     st -= len;
+     s += len;
+     d += len;
+    }
+   }
+   dest += rasterlen;
+   src += rasterlen;
+   j++;
+  }
+  kfree (buffer);
+ }
+}
+
+
+
+static void init_bresenham (struct fb_dda *p, short x0, short y0, short x1, short y1)
+{
+ if (!p)
+  return;
+
+ p->x = p->xprev = x0;
+ p->y = p->yprev = y0;
+ p->s1 = 1;
+ p->s2 = 1;
+
+ p->dx = x1 - x0;
+ if (p->dx < 0) {
+  p->dx = -p->dx;
+  p->s1 = -1;
+ }
+
+ p->dy = y1 - y0;
+ if (p->dy < 0) {
+  p->dy = -p->dy;
+  p->s2 = -1;
+ }
+
+ p->xchange = 0;
+
+ if (p->dy > p->dx) {
+  int tmp = p->dx;
+  p->dx = p->dy;
+  p->dy = tmp;
+  p->xchange = 1;
+ }
+
+ p->e = (p->dy<<1) - p->dx;
+ p->j = 0;
+}
+
+
+static void fb_enh_filltriangle (struct fb_info *info, struct fb_draw *p)
+{
+ struct fb_dda left;
+ struct fb_dda right;
+ struct fb_draw p2;
+ short xres, yres;
+
+ if (!info || !p)
+  return;
+ yres = info->var.yres;
+ if (p->x0 < 0 && p->x1 < 0 && p->x2 < 0)
+  return;
+ if (p->y0 < 0 && p->y1 < 0 && p->y2 < 0)
+  return;
+ if (p->y0 > p->y1) {
+  short tmp = p->x0; p->x0 = p->x1; p->x1 = tmp;
+  tmp = p->y0; p->y0 = p->y1; p->y1 = tmp;
+ }
+ if (p->y1 > p->y2) {
+  short tmp = p->x2; p->x2 = p->x1; p->x1 = tmp;
+  tmp = p->y2; p->y2 = p->y1; p->y1 = tmp;
+ }
+ if (p->y0 > p->y1) {
+  short tmp = p->x0; p->x0 = p->x1; p->x1 = tmp;
+  tmp = p->y0; p->y0 = p->y1; p->y1 = tmp;
+ }
+ if (p->y1 > p->y2) {
+  short tmp = p->x2; p->x2 = p->x1; p->x1 = tmp;
+  tmp = p->y2; p->y2 = p->y1; p->y1 = tmp;
+ }
+ xres = info->var.xres;
+ yres = info->var.yres;
+ if ((p->x0 >= xres && p->x1 >= xres && p->x2 >= xres) ||
+     (p->y0 >= yres && p->y1 >= yres && p->y2 >= yres))
+  return;
+ /*----------*/
+
+ init_bresenham (&left, p->x0, p->y0, p->x1, p->y1);
+ init_bresenham (&right, p->x0, p->y0, p->x2, p->y2);
+
+ if ((p2.clip_valid = p->clip_valid)) {
+  p2.clip_x0 = p->clip_x0;
+  p2.clip_y0 = p->clip_y0;
+  p2.clip_x1 = p->clip_x1;
+  p2.clip_y1 = p->clip_y1;
+ }
+
+ while (1) {
+  char got_ychange = 0;
+
+  if (left.xprev < right.xprev) {
+   p2.x0 = left.xprev;
+   p2.x1 = right.xprev;
+  } else {
+   p2.x1 = left.xprev;
+   p2.x0 = right.xprev;
+  }
+  p2.y0 = p2.y1 = left.yprev;
+  p2.color = p->color;
+  info->enhanced_ops->fb_fillrect (info, &p2);
+
+  /* Advance the left line */
+  left.xprev = left.x;
+  left.yprev = left.y;
+
+  while (!got_ychange) {
+   if (left.j == left.dx) {
+    if (left.y == p->y2)
+     return;
+    init_bresenham (&left, p->x1, p->y1, p->x2, p->y2);
+   }
+   left.j++;
+   if (left.e >= 0) {
+    if (left.xchange)
+     left.x += left.s1;
+    else {
+     got_ychange = 1;
+     left.y += left.s2;
+    }
+    left.e -= (left.dx << 1);
+   }
+   if (left.xchange) {
+    got_ychange = 1;
+    left.y += left.s2;
+   } else
+    left.x += left.s1;
+   left.e += (left.dy << 1);
+  }
+
+  right.xprev = right.x;
+  right.yprev = right.y;
+  got_ychange = 0;
+
+  while (!got_ychange) {
+   if (right.j == right.dx)
+    return;
+  
+   right.j++;
+   if (right.e >= 0) {
+    if (right.xchange)
+     right.x += right.s1;
+    else {
+     got_ychange = 1;
+     right.y += right.s2;
+    }
+    right.e -= (right.dx << 1);
+   }
+   if (right.xchange) {
+    got_ychange = 1;
+    right.y += right.s2;
+   } else
+    right.x += right.s1;
+   right.e += (right.dy << 1);
+  }
+ }
+}
+
+
+static u32 fb_enh_readpoint (struct fb_info *info, short x, short y)
+{
+ u32 bytes_per_pixel, offset, value;
+ unsigned char *ptr;
+
+ if (!info || info->state != FBINFO_STATE_RUNNING)
+  return RGB_TRANSPARENT;
+ if (x < 0 || y < 0 || x >= info->var.xres || y >= info->var.yres)
+  return RGB_TRANSPARENT;
+ /*----------*/
+
+        bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+        offset = y * info->fix.line_length + x * bytes_per_pixel;
+        ptr = offset + ((unsigned char*)info->screen_base);
+
+ value = 0;
+ switch (bytes_per_pixel) {
+ case 4:
+  value = fb_readl (ptr);
+  break;
+
+ case 3: {
+  u32 tmp;
+  value = 0xff & fb_readb (ptr);
+  ptr++;
+  tmp = 0xff & fb_readb (ptr);
+  ptr++;
+  tmp <<= 8;
+  value |= tmp;
+  tmp = 0xff & fb_readb (ptr);
+  tmp <<= 16;
+  value |= tmp;
+  break;
+ }
+
+ case 2:
+  value = fb_readw (ptr);
+  break;
+
+ default:
+  return RGB_TRANSPARENT;
+ }
+
+ return fb_pixel_to_rgb (info, value);
+}
+
+
+static u32 fb_enh_getpixels (struct fb_info *info, struct fb_put *p)
+{
+ short xres, yres;
+ u32 *ptr;
+ short i;
+
+ if (!info || !p || !p->pixels)
+  return 0;
+ if (!info->enhanced_ops->fb_readpoint)
+  return 0;
+ if (p->width <= 0 || p->x0 < 0 || p->y0 < 0)
+  return 0;
+ if (p->location != FB_LOCATION_KERNEL)
+  return 0;
+ ptr = (u32*) p->pixels;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ if (p->x0 >= xres)
+  return 0;
+ if (p->y0 >= yres)
+  return 0;
+ if (p->x0 + p->width >= xres)
+  p->width = xres - p->x0;
+ /*----------*/
+
+ i = p->width;
+ while (i > 0) {
+  u32 rgb = info->enhanced_ops->fb_readpoint (info, p->x0, p->y0);
+  *ptr++ = rgb;
+  p->x0++;
+  i--;
+ }
+ return p->width;
+}
+
+
+static void fb_enh_line (struct fb_info *info, struct fb_draw *p)
+{
+ struct fb_dda dda;
+ short x0_save, x1_save, y0_save, y1_save;
+ u8 use_clip=0;
+ short xres, yres;
+
+ if (!info || !p)
+  return;
+ if (!info->enhanced_ops->fb_fillrect)
+  return;
+ if ((p->x0 < 0 && p->x1 < 0) || (p->y0 < 0 && p->y1 < 0))
+  return;
+ xres = info->var.xres;
+ yres = info->var.yres;
+ if ((p->x0 >= xres && p->x1 >= xres) ||
+     (p->y0 >= yres && p->y1 >= yres))
+  return;
+ /*----------*/
+
+ init_bresenham (&dda, p->x0, p->y0, p->x1, p->y1);
+
+ x0_save = p->x0;
+ x1_save = p->x1;
+ y0_save = p->y0;
+ y1_save = p->y1;
+ use_clip = p->clip_valid;
+ p->clip_valid = 0;
+
+ while (dda.j <= dda.dx) {
+  dda.j++;
+
+  if (!use_clip ||
+      (dda.x >= p->clip_x0 &&
+       dda.x <= p->clip_x1 &&
+       dda.y >= p->clip_y0 &&
+       dda.y <= p->clip_y1)) {
+   p->x0 = p->x1 = dda.x;
+   p->y0 = p->y1 = dda.y;
+   info->enhanced_ops->fb_fillrect (info, p);
+  }
+
+  if (dda.e >= 0) {
+   if (dda.xchange)
+    dda.x += dda.s1;
+   else
+    dda.y += dda.s2;
+   dda.e -= (dda.dx << 1);
+  }
+  if (dda.xchange)
+   dda.y += dda.s2;
+  else
+   dda.x += dda.s1;
+  dda.e += (dda.dy << 1);
+ }
+
+ p->x0 = x0_save;
+ p->x1 = x1_save;
+ p->y0 = y0_save;
+ p->y1 = y1_save;
+ p->clip_valid = use_clip;
+}
+
+
+static void announce (struct fb_info *info)
+{
+ int height = 80;
+
+ if (!info->enhanced_ops) {
+  printk(KERN_INFO "Enhanced functionality routines not installed yet.\n");
+ } else {
+
+  /* Leave an indicator that the Enhanced functionality is available.
+   * Will be updated for rotated displays later...
+   */
+  if (info->enhanced_ops->fb_line) {
+   struct fb_draw params;
+   int y=0;
+   params.y0 = 0;
+   params.x1 = info->var.xres - 1;
+   while (y < height) {
+    u32 n = 0xff & ((y<<8)/height);
+    params.x0 = params.x1 - (y+y);
+    params.y1 = height - y - 1;
+    params.color = n + ((255-n)<<8);
+    params.clip_valid = 0;
+    info->enhanced_ops->fb_line (info, &params);
+    y += 4;
+   }
+  }
+#if 0
+  if (info->enhanced_ops->fb_filltriangle) {
+   struct fb_draw params;
+   params.x0 = info->var.xres - height;
+   params.y0 = height / 3;
+   params.x1 = params.x0 - height/6;
+   params.y1 = height - 1;
+   params.x2 = params.x0 + height/6;
+   params.y2 = height - 1;
+   params.color = 0xff0000;
+   info->enhanced_ops->fb_filltriangle (info, &params);
+   params.y0 = height - 1;
+   params.x1 = params.x0 - height/12;
+   params.x2 = params.x0 + height/12;
+   params.y1 = (2 * height) / 3;
+   params.y2 = params.y1;
+   params.color = 0xffA500;
+   info->enhanced_ops->fb_filltriangle (info, &params);
+   params.x0 = info->var.xres - height;
+   params.y0 = params.y1;
+   params.x1 = params.x0 - height/24;
+   params.x2 = params.x0 + height/24;
+   params.y1 = (5 * height) / 6;
+   params.y2 = params.y1;
+   params.color = 0xffff00;
+   info->enhanced_ops->fb_filltriangle (info, &params);
+  }
+#endif
+ }
+}
+
+static int fb_enhanced_install (struct fb_info *info)
+{
+ struct fb_enhanced_ops *ops;
+
+ if (!info)
+  return 0;
+
+ ops = kmalloc(sizeof(struct fb_enhanced_ops), GFP_KERNEL);
+ if (!ops) {
+  printk(KERN_ERR "fb_enhanced_install: kmalloc failed\n");
+  return 0;
+ }
+
+ memset (ops, 0, sizeof(struct fb_enhanced_ops));
+
+ ops->fb_line = fb_enh_line;
+ ops->fb_fillrect = fb_enh_fillrect;
+ ops->fb_filltriangle = fb_enh_filltriangle;
+ ops->fb_copyarea = fb_enh_copyarea;
+ ops->fb_putimage = fb_enh_putimage;
+ ops->fb_readpoint = fb_enh_readpoint;
+ ops->fb_getpixels = fb_enh_getpixels;
+
+ printk(KERN_INFO "Enhanced Functionality available.\n");
+
+ info->enhanced_ops = ops;
+
+ announce(info);
+
+ return 1;
+}
+
+static int fb_enhanced_event_notify(struct notifier_block *self,
+          unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ struct fb_videomode *mode;
+ int ret = 0;
+
+ switch(action) {
+ case FB_EVENT_SUSPEND:
+printk(KERN_INFO "Enhanced Functionality got event Suspend\n");
+  break;
+ case FB_EVENT_RESUME:
+printk(KERN_INFO "Enhanced Functionality got event Resume\n");
+  break;
+ case FB_EVENT_MODE_CHANGE:
+printk(KERN_INFO "Enhanced Functionality got event ModeChange\n");
+  break;
+ case FB_EVENT_MODE_CHANGE_ALL:
+printk(KERN_INFO "Enhanced Functionality got event ModeChangeAll\n");
+  break;
+ case FB_EVENT_MODE_DELETE:
+printk(KERN_INFO "Enhanced Functionality got event ModeDelete\n");
+  mode = event->data;
+  break;
+ case FB_EVENT_FB_REGISTERED:
+printk(KERN_INFO "Enhanced Functionality got event Registered\n");
+  fb_enhanced_install (info);
+  break;
+ case FB_EVENT_SET_CONSOLE_MAP:
+printk(KERN_INFO "Enhanced Functionality got event SetConsoleMap\n");
+  break;
+ case FB_EVENT_GET_CONSOLE_MAP:
+printk(KERN_INFO "Enhanced Functionality got event GetConsoleMap\n");
+  break;
+ case FB_EVENT_BLANK:
+printk(KERN_INFO "Enhanced Functionality got event Blank\n");
+  break;
+ case FB_EVENT_NEW_MODELIST:
+printk(KERN_INFO "Enhanced Functionality got event ModeList\n");
+  break;
+ case FB_EVENT_SET_CON_ROTATE:
+printk(KERN_INFO "Enhanced Functionality got event SetConRotate\n");
+  break;
+ case FB_EVENT_GET_CON_ROTATE:
+printk(KERN_INFO "Enhanced Functionality got event GetConRotate\n");
+  break;
+ case FB_EVENT_SET_CON_ROTATE_ALL:
+printk(KERN_INFO "Enhanced Functionality got event GetConRotateAll\n");
+ }
+
+ return ret;
+}
+
+
+static struct notifier_block fb_enhanced_event_notifier = {
+ .notifier_call = fb_enhanced_event_notify,
+};
+
+static int __init fb_enh_init(void)
+{
+ acquire_console_sem();
+ fb_register_client(&fb_enhanced_event_notifier);
+ release_console_sem();
+
+ return 0;
+}
+
+module_init(fb_enh_init);
+
+#ifdef MODULE
+
+static void __exit fb_enh_exit(void)
+{
+ acquire_console_sem();
+ fb_unregister_client(&fb_enhanced_event_notifier);
+ release_console_sem();
+} 
+
+module_exit(fb_enh_exit);
+
+#endif
+
+EXPORT_SYMBOL(fb_combine_rgb_pixels);
+EXPORT_SYMBOL(fb_pixel_from_rgb);
+EXPORT_SYMBOL(fb_pixel_to_rgb);
+
+#endif
+/*------------------ End of Advanced Functionality ----------------*/
+
+MODULE_LICENSE("GPL");
diff -Naur linux-2.6.15.4-original/include/linux/fb.h linux-2.6.15.4/include/linux/fb.h
--- linux-2.6.15.4-original/include/linux/fb.h 2006-02-10 07:22:48.000000000 +0000
+++ linux-2.6.15.4/include/linux/fb.h 2006-02-24 13:01:20.000000000 +0000
@@ -2,6 +2,7 @@
 #define _LINUX_FB_H
 
 #include <asm/types.h>
+#include <linux/fb_enhanced.h>
 
 /* Definitions of frame buffers      */
 
@@ -762,6 +763,7 @@
 #ifdef CONFIG_FB_TILEBLITTING
  struct fb_tile_ops *tileops;    /* Tile Blitting */
 #endif
+ struct fb_enhanced_ops *enhanced_ops; /* If NULL, module not loaded */
  char __iomem *screen_base; /* Virtual address */
  unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
  void *pseudo_palette;  /* Fake palette of 16 colors */
diff -Naur linux-2.6.15.4-original/include/linux/fb_enhanced.h linux-2.6.15.4/include/linux/fb_enhanced.h
--- linux-2.6.15.4-original/include/linux/fb_enhanced.h 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15.4/include/linux/fb_enhanced.h 2006-02-26 12:10:27.000000000 +0000
@@ -0,0 +1,123 @@
+
+/* Enhanced Functionality
+ * Copyright (C) 2004-2006 by Zachary T Smith, fbui@comcast.net
+ * This software is covered by the GNU Public License,
+ * see the file COPYING for more information.
+ */
+
+#ifndef _LINUX_FB_ENHANCED_H
+#define _LINUX_FB_ENHANCED_H
+
+#include <asm/types.h>
+#define FB_IMAGETYPE_RGB2 0 /* 16 bpp (5-6-5) */
+#define FB_IMAGETYPE_RGB3 1 /* 24 bpp (8-8-8) */
+#define FB_IMAGETYPE_RGB4 2 /* 24 bpp (8-8-8) + 8b ignored */
+#define FB_IMAGETYPE_RGBA 3 /* 24 bpp (8-8-8) + 8b transparency */
+#define FB_IMAGETYPE_GREY 4 /* 8 bits grey => 24 bits RGB */
+#define FB_IMAGETYPE_MONO 5 /* 1 bpp, 1=>color 0=>transparent */
+#define FB_IMAGETYPE_ALPHA 6 /* 8 bits alpha plus foreground color */
+
+/* Some useful colors */
+#define RGB_NOCOLOR 0xff000000
+#define RGB_TRANSPARENT 0xff000000
+#define RGB_BLACK  0
+#define RGB_GRAY 0xa0a0a0
+#define RGB_GREY 0xa0a0a0
+#define RGB_WHITE  0xffffff
+#define RGB_RED  0xff0000
+#define RGB_GREEN 0xff00
+#define RGB_DARKGREEN 0xA000
+#define RGB_BLUE 0xff
+#define RGB_NAVYBLUE 0x80
+#define RGB_CYAN 0xffff
+#define RGB_YELLOW 0xffff00
+#define RGB_MAGENTA 0xff00ff
+#define RGB_ORANGE  0xffa000
+#define RGB_PURPLE 0xa030ff
+#define RGB_LTBROWN 0xb54c4c
+#define RGB_BROWN 0xa52c2c
+#define RGB_STEELBLUE  0x4682B4
+#define RGB_SIENNA 0x605230
+
+struct fb_draw {  /* Used for pixel-level drawing */
+ short x0;
+ short y0;
+ short x1;
+ short y1;
+ unsigned long color;
+ short x2; /* used by copyarea, fill_triangle */
+ short y2;
+ short clip_x0;
+ short clip_y0;
+ short clip_x1;
+ short clip_y1;
+ unsigned int clip_valid : 1;
+ /* unsigned long color1, color2; */
+};
+
+#define FB_LOCATION_KERNEL 0 /* kernel space */
+#define FB_LOCATION_USER 1 /* user space */
+#define FB_LOCATION_VRAM 2 /* video RAM */
+
+struct fb_put {  /* Used to drawing images */
+ short x0;
+ short y0;
+ short x1;
+ short y1;
+ short width;
+ short height;
+ short xstart;
+ short xend;
+ short ystart;
+ short yend;
+ unsigned char *pixels;
+ short clip_x0;
+ short clip_y0;
+ short clip_x1;
+ short clip_y1;
+ u32 color; /* for mono & alpha only */
+
+ unsigned int unused__ : 8;
+ unsigned int type : 5;
+ unsigned int location : 2; /* source location */
+ unsigned int clip_valid : 1;
+};
+
+
+#ifdef __KERNEL__
+
+/* Bresenham algorithm data */
+struct fb_dda { 
+ short x, y;
+ short dx, dy;
+ short j, e, s1, s2, xchange;
+ short xprev, yprev;
+};
+
+struct fb_info;
+
+struct fb_enhanced_ops {
+        void (*fb_line) (struct fb_info *info, struct fb_draw *params);
+
+ /* Clippable and alpha-compositable: draw a 1-color filled triangle */
+        void (*fb_filltriangle) (struct fb_info *info, struct fb_draw *params);
+
+ /* Clippable and alpha-compositable: draw a filled rectangle */
+        void (*fb_fillrect) (struct fb_info *info, struct fb_draw *params);
+
+ /* Clippable: copy area, on-screen only */
+ void (*fb_copyarea) (struct fb_info *, struct fb_draw *params);
+
+ /* Clippable: put an image of any of several types */
+ void (*fb_putimage) (struct fb_info *, struct fb_put *params);
+
+ /* Clippable: read a point if visible */
+        u32 (*fb_readpoint) (struct fb_info *info, short x, short y);
+
+ /* Clippable: read RGB pixels as ulongs in a row if visible */
+ u32 (*fb_getpixels) (struct fb_info *, struct fb_put *params);
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_FB_ENHANCED_H */