[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH] m68k Atari: experimental atafb support



Hi all,

regardless of current distribution status, here's my experimental patch
for atafb in 2.6.17. The result looks the same on real hardware (Falcon)
as it does on the ARAnyM emulator. Only really supported mode is vga256
for now. The code is really ugly, and there's some interaction between the
modedb entries and what happens on screen that I really don't understand.

What works: very basic support for iplan2p8 mode. Compare the old
fbcon_iplan2p8 module with the new atafb_iplan2p8 one to see what I did.
Similar work needs to be done for iplan2p4 and iplan2p82 and mfb modules.
I'm planning to stick function pointers to the appropriate drawing modules
into the par struct when that's done. Any help is appreciated. Maybe
Petr's atafb specialist is interested in poking around there.

Something's odd with alignment of the start address in the linefill
function - sometimes the start address isn't properly aligned and the
characters get smeared across different color planes. Problem is, I still
haven't fully grokked the pixel format :-(

The good news: with an IDE disk attached, the kernel boots normally and I
can log in and play around. No network, and no SCSI driver yet, so I don't
reckon we can drop 2.4 anytime soon. But we're getting there.

Note to CT60 users: I had to disable setting any keyboard ACIA bits except
ACIA_RESET or I'd get an access fault on the ACIA - just like I reported
for 2.6.13. Look at arch/m68k/atari/config.c and arch/m68k/atari/atakeyb.c
to see what I mean.

Note to m68k-build: the box is back in service as buildd now.

	Michael
--- linux-2.6.17-m68k-vanilla/drivers/video/atafb.c	2006-04-09 18:56:02.000000000 +0200
+++ linux-2.6.17-m68k/drivers/video/atafb.c	2006-09-22 18:32:38.271846684 +0200
@@ -40,6 +40,9 @@
  *   - For the Falcon it is not possible to set random video modes on
  *     SM124 and SC/TV, only the bootup resolution is supported.
  *
+ *	22 Sep 06: MSch: kernel 2.6.17 porting; fillrect, copyarea and imageblit
+ *		   shamelessly stolen from the amifb driver. At least I get some
+ *		   console output now :-)
  */
 
 #define ATAFB_TT
@@ -72,6 +75,7 @@
 #include <linux/fb.h>
 #include <asm/atarikb.h>
 
+#if 0
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb16.h>
@@ -79,7 +83,10 @@
 #include <video/fbcon-iplan2p4.h>
 #include <video/fbcon-iplan2p8.h>
 #include <video/fbcon-mfb.h>
+#endif
 
+#include "c2p.h"
+#include "atafb_iplan2p8.h"
 
 #define SWITCH_ACIA 0x01		/* modes for switch on OverScan */
 #define SWITCH_SND6 0x40
@@ -89,6 +96,27 @@
 
 #define up(x, r) (((x) + (r) - 1) & ~((r)-1))
 
+	/*
+	 * Interface to the world
+	 */
+
+static int atafb_check_var(struct fb_var_screeninfo *var,
+			   struct fb_info *info);
+static int atafb_set_par(struct fb_info *info);
+static int atafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info);
+static int atafb_blank(int blank, struct fb_info *info);
+static int atafb_pan_display(struct fb_var_screeninfo *var,
+			     struct fb_info *info);
+static void atafb_fillrect(struct fb_info *info,
+			   const struct fb_fillrect *rect);
+static void atafb_copyarea(struct fb_info *info,
+			   const struct fb_copyarea *region);
+static void atafb_imageblit(struct fb_info *info,
+			    const struct fb_image *image);
+static int atafb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg);
+
 
 static int default_par=0;	/* default resolution (0=none) */
 
@@ -102,9 +130,15 @@
 static int sttt_xres_virtual=640,sttt_yres_virtual=400;
 static int ovsc_offset=0, ovsc_addlen=0;
 
+	/*
+	 * Hardware parameters for current mode
+	 */
+
 static struct atafb_par {
 	void *screen_base;
 	int yres_virtual;
+	u_long next_line;
+	u_long next_plane;
 #if defined ATAFB_TT || defined ATAFB_STE
 	union {
 		struct {
@@ -137,6 +171,8 @@
 	} hw;
 } current_par;
 
+struct fb_var_screeninfo current_var;
+
 /* Don't calculate an own resolution, and thus don't change the one found when
  * booting (currently used for the Falcon to keep settings for internal video
  * hardware extensions (e.g. ScreenBlaster)  */
@@ -165,7 +201,13 @@
 #define VMO_PREMASK		0x0c
 #endif
 
-static struct fb_info fb_info;
+static struct fb_info fb_info = {
+    .fix = {
+        .id             = "Atari ",
+	.visual         = FB_VISUAL_PSEUDOCOLOR,
+	.accel          = FB_ACCEL_NONE
+    }
+};
 
 static void *screen_base;	/* base address of screen */
 static void *real_screen_base;	/* (only for Overscan) */
@@ -176,8 +218,6 @@
 
 static int mono_moni=0;
 
-static struct display disp;
-
 
 #ifdef ATAFB_EXT
 /* external video handling */
@@ -251,6 +291,75 @@
 extern int fontwidth_8x16;
 extern unsigned char fontdata_8x16[];
 
+/* 
+ * struct fb_ops {
+ *	* open/release and usage marking 
+ *	struct module *owner;
+ *	int (*fb_open)(struct fb_info *info, int user);
+ *	int (*fb_release)(struct fb_info *info, int user);
+ *
+ *	* For framebuffers with strange non linear layouts or that do not
+ *	* work with normal memory mapped access
+ *	ssize_t (*fb_read)(struct file *file, char __user *buf, size_t count, loff_t *ppos);
+ *	ssize_t (*fb_write)(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
+ *
+ *	* checks var and eventually tweaks it to something supported,
+ *	* DOES NOT MODIFY PAR *
+ *	int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
+ *
+ *	* set the video mode according to info->var *
+ *	int (*fb_set_par)(struct fb_info *info);
+ *
+ *	* set color register *
+ *	int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
+ *			    unsigned blue, unsigned transp, struct fb_info *info);
+ *
+ *	* set color registers in batch *
+ *	int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
+ *
+ *	* blank display *
+ *	int (*fb_blank)(int blank, struct fb_info *info);
+ *
+ *	* pan display *
+ *	int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
+ *
+ *	*** The meat of the drawing engine ***
+ *	* Draws a rectangle *
+ *	void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
+ *	* Copy data from area to another *
+ *	void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
+ *	* Draws a image to the display *
+ *	void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
+ *
+ *	* Draws cursor *
+ *	int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
+ *
+ *	* Rotates the display *
+ *	void (*fb_rotate)(struct fb_info *info, int angle);
+ *
+ *	* wait for blit idle, optional *
+ *	int (*fb_sync)(struct fb_info *info);
+ *
+ *	* perform fb specific ioctl (optional) *
+ *	int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
+ *			unsigned long arg);
+ *
+ *	* Handle 32bit compat ioctl (optional) *
+ *	int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
+ *			unsigned long arg);
+ *
+ *	* perform fb specific mmap *
+ *	int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
+ *
+ *	* save current hardware state *
+ *	void (*fb_save_state)(struct fb_info *info);
+ *
+ *	* restore saved state *
+ *	void (*fb_restore_state)(struct fb_info *info);
+ * } ;
+ */
+
+
 /* ++roman: This structure abstracts from the underlying hardware (ST(e),
  * TT, or Falcon.
  *
@@ -264,6 +373,7 @@
  *                    struct atafb_par *par )
  *   This function should fill in the 'fix' structure based on the
  *   values in the 'par' structure.
+ * !!! Obsolete, perhaps !!!
  *   
  * int (*decode_var)( struct fb_var_screeninfo *var,
  *                    struct atafb_par *par )
@@ -280,6 +390,7 @@
  *   
  * void (*get_par)( struct atafb_par *par )
  *   Fill the hardware's 'par' structure.
+ *   !!! Used only by detect() !!!
  *   
  * void (*set_par)( struct atafb_par *par )
  *   Set the hardware according to 'par'.
@@ -418,6 +529,127 @@
 
 static int num_atafb_predefined=ARRAY_SIZE(atafb_predefined);
 
+	/*
+	 * Tags used to indicate a specific Pixel Clock
+	 *
+	 * tag is the shift value to get the timings in 35 ns units
+	 */
+
+enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
+
+/*
+ * Video mode deb stolen from amifb; needs adjusting! 
+ */
+
+static struct fb_videomode atafb_modedb[] __initdata = {
+
+    /*
+     *  Atari Video Modes
+     *
+     *  If you change these, make sure to update DEFMODE_* as well!
+     */
+     
+    {
+	/* 640x200, 15 kHz, 60 Hz (NTSC) */
+	"ntsc", 60, 640, 200, TAG_HIRES, 106, 86, 44, 16, 76, 2,
+	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
+	"ntsc-lace", 60, 640, 400, TAG_HIRES, 106, 86, 88, 33, 76, 4,
+	FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x256, 15 kHz, 50 Hz (PAL) */
+	"pal", 50, 640, 256, TAG_HIRES, 106, 86, 40, 14, 76, 2,
+	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
+	"pal-lace", 50, 640, 512, TAG_HIRES, 106, 86, 80, 29, 76, 4,
+	FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x480, 29 kHz, 57 Hz */
+	"multiscan", 57, 640, 480, TAG_SHRES, 96, 112, 29, 8, 72, 8,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x960, 29 kHz, 57 Hz interlaced */
+	"multiscan-lace", 57, 640, 960, TAG_SHRES, 96, 112, 58, 16, 72, 16,
+	0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x200, 15 kHz, 72 Hz */
+	"euro36", 72, 640, 200, TAG_HIRES, 92, 124, 6, 6, 52, 5,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x400, 15 kHz, 72 Hz interlaced */
+	"euro36-lace", 72, 640, 400, TAG_HIRES, 92, 124, 12, 12, 52, 10,
+	0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x400, 29 kHz, 68 Hz */
+	"euro72", 68, 640, 400, TAG_SHRES, 164, 92, 9, 9, 80, 8,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x800, 29 kHz, 68 Hz interlaced */
+	"euro72-lace", 68, 640, 800, TAG_SHRES, 164, 92, 18, 18, 80, 16,
+	0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 800x300, 23 kHz, 70 Hz */
+	"super72", 70, 800, 300, TAG_SHRES, 212, 140, 10, 11, 80, 7,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 800x600, 23 kHz, 70 Hz interlaced */
+	"super72-lace", 70, 800, 600, TAG_SHRES, 212, 140, 20, 22, 80, 14,
+	0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x200, 27 kHz, 57 Hz doublescan */
+	"dblntsc", 57, 640, 200, TAG_SHRES, 196, 124, 18, 17, 80, 4,
+	0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+    }, {
+	/* 640x400, 27 kHz, 57 Hz */
+	"dblntsc-ff", 57, 640, 400, TAG_SHRES, 196, 124, 36, 35, 80, 7,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x800, 27 kHz, 57 Hz interlaced */
+	"dblntsc-lace", 57, 640, 800, TAG_SHRES, 196, 124, 72, 70, 80, 14,
+	0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x256, 27 kHz, 47 Hz doublescan */
+	"dblpal", 47, 640, 256, TAG_SHRES, 196, 124, 14, 13, 80, 4,
+	0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+    }, {
+	/* 640x512, 27 kHz, 47 Hz */
+	"dblpal-ff", 47, 640, 512, TAG_SHRES, 196, 124, 28, 27, 80, 7,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x1024, 27 kHz, 47 Hz interlaced */
+	"dblpal-lace", 47, 640, 1024, TAG_SHRES, 196, 124, 56, 54, 80, 14,
+	0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+    },
+
+    /*
+     *  VGA Video Modes
+     */
+
+    {
+	/* 640x480, 31 kHz, 60 Hz (VGA) */
+	"vga", 60, 640, 480, TAG_SHRES, 64, 96, 30, 9, 112, 2,
+	0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    }, {
+	/* 640x400, 31 kHz, 70 Hz (VGA) */
+	"vga70", 70, 640, 400, TAG_SHRES, 64, 96, 35, 12, 112, 2,
+	FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+    },
+
+};
+
+#define NUM_TOTAL_MODES  ARRAY_SIZE(atafb_modedb)
+
+static char *mode_option __initdata = NULL;
+
+ /* default modes */
+
+#define DEFMODE_TT	    2	/* "pal" for PAL OCS/ECS */
+#define DEFMODE_F30	    0	/* "ntsc" for NTSC OCS/ECS */
+#define DEFMODE_STE   	    3	/* "pal-lace" for flicker fixed PAL (A3000) */
+#define DEFMODE_EXT  	    1	/* "ntsc-lace" for flicker fixed NTSC (A3000) */
+
 
 static int
 get_video_mode(char *vname)
@@ -1291,6 +1523,15 @@
 	par->screen_base = screen_base + var->yoffset * linelen;
 	par->hw.falcon.xoffset = 0;
 
+	// FIXME!!! sort of works, no crash
+	//par->next_line  = linelen;
+	//par->next_plane = yres_virtual * linelen;
+	par->next_line  = linelen;
+	par->next_plane = 2;
+	// crashes
+	//par->next_plane = linelen;
+	//par->next_line  = yres_virtual * linelen;
+
 	return 0;
 }
 
@@ -1584,10 +1825,20 @@
 
 
 static int falcon_pan_display( struct fb_var_screeninfo *var,
-							   struct atafb_par *par )
+			       struct fb_info *info )
 {
+	struct atafb_par *par=(struct atafb_par *)info->par;
+
 	int xoffset;
-	int bpp = fb_display[fb_info.currcon].var.bits_per_pixel;
+	int bpp = info->var.bits_per_pixel;
+
+	if (!par) {
+		printk("falcon_pan_display: called from %p\n",
+			__builtin_return_address(0));
+		printk("falcon_pan_display: info %p var %p info->var %p\n",
+			info, var, &(info->var));
+		panic("NULL par in falcon_pan_display");
+	}
 
 	if (bpp == 1)
 		var->xoffset = up(var->xoffset, 32);
@@ -1598,13 +1849,13 @@
 		var->xoffset = up(var->xoffset, 2);
 	}
 	par->hw.falcon.line_offset = bpp *
-	       	(fb_display[fb_info.currcon].var.xres_virtual - fb_display[fb_info.currcon].var.xres) / 16;
+	       	(info->var.xres_virtual - info->var.xres) / 16;
 	if (par->hw.falcon.xoffset)
 		par->hw.falcon.line_offset -= bpp;
 	xoffset = var->xoffset - par->hw.falcon.xoffset;
 
 	par->screen_base = screen_base +
-	        (var->yoffset * fb_display[fb_info.currcon].var.xres_virtual + xoffset) * bpp / 8;
+	        (var->yoffset * info->var.xres_virtual + xoffset) * bpp / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -2295,15 +2546,17 @@
 
 
 static int pan_display( struct fb_var_screeninfo *var,
-                        struct atafb_par *par )
+                        struct fb_info *info )
 {
+	struct atafb_par *par=(struct atafb_par *)info->par;
+
 	if (!fbhw->set_screen_base ||
 		(!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset))
 		return -EINVAL;
 	var->xoffset = up(var->xoffset, 16);
 	par->screen_base = screen_base +
-	        (var->yoffset * fb_display[fb_info.currcon].var.xres_virtual + var->xoffset)
-	        * fb_display[fb_info.currcon].var.bits_per_pixel / 8;
+	        (var->yoffset * info->var.xres_virtual + var->xoffset)
+	        * info->var.bits_per_pixel / 8;
 	fbhw->set_screen_base (par->screen_base);
 	return 0;
 }
@@ -2345,7 +2598,7 @@
 
 
 
-static void atafb_get_par( struct atafb_par *par )
+static void ata_get_par( struct atafb_par *par )
 {
 	if (current_par_valid) {
 		*par=current_par;
@@ -2355,7 +2608,7 @@
 }
 
 
-static void atafb_set_par( struct atafb_par *par )
+static void ata_set_par( struct atafb_par *par )
 {
 	fbhw->set_par(par);
 	current_par=*par;
@@ -2372,10 +2625,10 @@
 /* used for hardware scrolling */
 
 static int
-fb_update_var(int con, struct fb_info *info)
+fb_update_var(struct fb_info *info)
 {
-	int off=fb_display[con].var.yoffset*fb_display[con].var.xres_virtual*
-			fb_display[con].var.bits_per_pixel>>3;
+	int off=info->var.yoffset*info->var.xres_virtual*
+			info->var.bits_per_pixel>>3;
 
 	current_par.screen_base=screen_base + off;
 
@@ -2393,86 +2646,70 @@
 		return err;
 	activate=var->activate;
 	if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
-		atafb_set_par(&par);
+		ata_set_par(&par);
 	fbhw->encode_var(var, &par);
 	var->activate=activate;
 	return 0;
 }
 
+// FIXME: con no longer used; take current var from info instead
+
 static int
-atafb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+atafb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info)
 {
 	struct atafb_par par;
-	if (con == -1)
-		atafb_get_par(&par);
-	else {
 	  int err;
-		if ((err=fbhw->decode_var(&fb_display[con].var,&par)))
+	// ??? Get fix directly (case con == -1 before)?? 
+	if ((err=fbhw->decode_var(&info->var,&par)))
 		  return err;
-	}
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
 	return fbhw->encode_fix(fix, &par);
 }
 	
 static int
-atafb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+atafb_get_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
 	struct atafb_par par;
-	if (con == -1) {
-		atafb_get_par(&par);
+
+	// ??? 
+	ata_get_par(&par);
 		fbhw->encode_var(var, &par);
-	}
-	else
-		*var=fb_display[con].var;
+
 	return 0;
 }
 
+// REMOVE !!! No longer called by fbcon!
+// Still called by set_var internally
+
 static void
-atafb_set_disp(int con, struct fb_info *info)
+atafb_set_disp(struct fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
 	struct fb_var_screeninfo var;
-	struct display *display;
 
-	if (con >= 0)
-		display = &fb_display[con];
-	else
-		display = &disp;	/* used during initialization */
+	// set fix / var directly on fb_info unions instead
+
+	atafb_get_fix(&fix, info);
+	atafb_get_var(&var, info);
 
-	atafb_get_fix(&fix, con, info);
-	atafb_get_var(&var, con, info);
-	if (con == -1)
-		con=0;
 	info->screen_base = (void *)fix.smem_start;
-	display->visual = fix.visual;
-	display->type = fix.type;
-	display->type_aux = fix.type_aux;
-	display->ypanstep = fix.ypanstep;
-	display->ywrapstep = fix.ywrapstep;
-	display->line_length = fix.line_length;
-	if (fix.visual != FB_VISUAL_PSEUDOCOLOR &&
-		fix.visual != FB_VISUAL_DIRECTCOLOR)
-		display->can_soft_blank = 0;
-	else
-		display->can_soft_blank = 1;
-	display->inverse =
-	    (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+
 	switch (fix.type) {
 	    case FB_TYPE_INTERLEAVED_PLANES:
 		switch (var.bits_per_pixel) {
 #ifdef FBCON_HAS_IPLAN2P2
 		    case 2:
-			display->dispsw = &fbcon_iplan2p2;
+			// display->dispsw = &fbcon_iplan2p2;
 			break;
 #endif
 #ifdef FBCON_HAS_IPLAN2P4
 		    case 4:
-			display->dispsw = &fbcon_iplan2p4;
+			// display->dispsw = &fbcon_iplan2p4;
 			break;
 #endif
 #ifdef FBCON_HAS_IPLAN2P8
 		    case 8:
-			display->dispsw = &fbcon_iplan2p8;
+			// display->dispsw = &fbcon_iplan2p8;
 			break;
 #endif
 		}
@@ -2481,18 +2718,18 @@
 		switch (var.bits_per_pixel) {
 #ifdef FBCON_HAS_MFB
 		    case 1:
-			display->dispsw = &fbcon_mfb;
+			// display->dispsw = &fbcon_mfb;
 			break;
 #endif
 #ifdef FBCON_HAS_CFB8
 		    case 8:
-			display->dispsw = &fbcon_cfb8;
+			// display->dispsw = &fbcon_cfb8;
 			break;
 #endif
 #ifdef FBCON_HAS_CFB16
 		    case 16:
-			display->dispsw = &fbcon_cfb16;
-			display->dispsw_data = fbcon_cfb16_cmap;
+			// display->dispsw = &fbcon_cfb16;
+			// display->dispsw_data = fbcon_cfb16_cmap;
 			break;
 #endif
 		}
@@ -2505,25 +2742,26 @@
 {
 	int err,oldxres,oldyres,oldbpp,oldxres_virtual,
 	    oldyres_virtual,oldyoffset;
-	if ((err=do_fb_set_var(var, con==info->currcon)))
+	if ((err=do_fb_set_var(var, 1)))
 		return err;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
-		oldxres=fb_display[con].var.xres;
-		oldyres=fb_display[con].var.yres;
-		oldxres_virtual=fb_display[con].var.xres_virtual;
-		oldyres_virtual=fb_display[con].var.yres_virtual;
-		oldbpp=fb_display[con].var.bits_per_pixel;
-		oldyoffset=fb_display[con].var.yoffset;
-		fb_display[con].var=*var;
+		oldxres=info->var.xres;
+		oldyres=info->var.yres;
+		oldxres_virtual=info->var.xres_virtual;
+		oldyres_virtual=info->var.yres_virtual;
+		oldbpp=info->var.bits_per_pixel;
+		oldyoffset=info->var.yoffset;
+		info->var=*var;
 		if (oldxres != var->xres || oldyres != var->yres 
 		    || oldxres_virtual != var->xres_virtual
 		    || oldyres_virtual != var->yres_virtual
 		    || oldbpp != var->bits_per_pixel
 		    || oldyoffset != var->yoffset) {
-			atafb_set_disp(con, info);
-			(*fb_info.changevar)(con);
-			fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
-			do_install_cmap(con, info);
+			atafb_set_disp(info);
+			// ??? 
+			// (*fb_info.changevar)(con);
+			// fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
+			// do_install_cmap(con, info);
 		}
 	}
 	var->activate=0;
@@ -2531,45 +2769,830 @@
 }
 
 
-
+#if 0
 static int
-atafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+atafb_get_cmap(struct fb_cmap *cmap, int kspc, struct fb_info *info)
 {
-	if (con == info->currcon) /* current console ? */
 		return fb_get_cmap(cmap, kspc, fbhw->getcolreg, info);
-	else
-		if (fb_display[con].cmap.len) /* non default colormap ? */
-			fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
-		else
-			fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-				     cmap, kspc ? 0 : 2);
-	return 0;
 }
+#endif
+
+#define upx(x,v)        (((v)+(x)-1) & -(x))
 
 static int
-atafb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+atafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
 {
 	int xoffset = var->xoffset;
 	int yoffset = var->yoffset;
 	int err;
 
-	if (   xoffset < 0 || xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual
-	    || yoffset < 0 || yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+        if (var->vmode & FB_VMODE_YWRAP) {
+		if (var->yoffset < 0 ||
+		    var->yoffset >= info->var.yres_virtual || var->xoffset)
+			return -EINVAL;
+	} else {                                           
+		if (var->xoffset+info->var.xres > info->var.xres_virtual ||
+		    var->yoffset+info->var.yres > info->var.yres_virtual)
 		return -EINVAL;
+	}
+
+    	//if (   xoffset < 0 || xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual
+	//    || yoffset < 0 || yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+	//	return -EINVAL;
 
-	if (con == info->currcon) {
 		if (fbhw->pan_display) {
-			if ((err = fbhw->pan_display(var, &current_par)))
+		if ((err = fbhw->pan_display(var, info)))
 				return err;
 		}
 		else
 			return -EINVAL;
-	}
-	fb_display[con].var.xoffset = var->xoffset;
-	fb_display[con].var.yoffset = var->yoffset;
+
+	info->var.xoffset = var->xoffset;
+	info->var.yoffset = var->yoffset;
+
+	if (var->vmode & FB_VMODE_YWRAP)
+        	info->var.vmode |= FB_VMODE_YWRAP;
+        else
+                info->var.vmode &= ~FB_VMODE_YWRAP;
+                
 	return 0;
 }
 
+#if BITS_PER_LONG == 32
+#define BYTES_PER_LONG	4
+#define SHIFT_PER_LONG	5
+#elif BITS_PER_LONG == 64
+#define BYTES_PER_LONG	8
+#define SHIFT_PER_LONG	6
+#else
+#define Please update me
+#endif
+
+
+    /*
+     *  Compose two values, using a bitmask as decision value
+     *  This is equivalent to (a & mask) | (b & ~mask)
+     */
+
+static inline unsigned long comp(unsigned long a, unsigned long b,
+				 unsigned long mask)
+{
+	return ((a ^ b) & mask) ^ b;
+}
+
+
+static inline unsigned long xor(unsigned long a, unsigned long b,
+				unsigned long mask)
+{
+	return (a & mask) ^ b;
+}
+
+
+    /*
+     *  Unaligned forward bit copy using 32-bit or 64-bit memory accesses
+     */
+
+static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
+		   int src_idx, u32 n)
+{
+	unsigned long first, last;
+	int shift = dst_idx-src_idx, left, right;
+	unsigned long d0, d1;
+	int m;
+
+	if (!n)
+		return;
+
+	shift = dst_idx-src_idx;
+	first = ~0UL >> dst_idx;
+	last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
+
+	if (!shift) {
+		// Same alignment for source and dest
+
+		if (dst_idx+n <= BITS_PER_LONG) {
+			// Single word
+			if (last)
+				first &= last;
+			*dst = comp(*src, *dst, first);
+		} else {
+			// Multiple destination words
+			// Leading bits
+			if (first) {
+				*dst = comp(*src, *dst, first);
+				dst++;
+				src++;
+				n -= BITS_PER_LONG-dst_idx;
+			}
+
+			// Main chunk
+			n /= BITS_PER_LONG;
+			while (n >= 8) {
+				*dst++ = *src++;
+				*dst++ = *src++;
+				*dst++ = *src++;
+				*dst++ = *src++;
+				*dst++ = *src++;
+				*dst++ = *src++;
+				*dst++ = *src++;
+				*dst++ = *src++;
+				n -= 8;
+			}
+			while (n--)
+				*dst++ = *src++;
+
+			// Trailing bits
+			if (last)
+				*dst = comp(*src, *dst, last);
+		}
+	} else {
+		// Different alignment for source and dest
+
+		right = shift & (BITS_PER_LONG-1);
+		left = -shift & (BITS_PER_LONG-1);
+
+		if (dst_idx+n <= BITS_PER_LONG) {
+			// Single destination word
+			if (last)
+				first &= last;
+			if (shift > 0) {
+				// Single source word
+				*dst = comp(*src >> right, *dst, first);
+			} else if (src_idx+n <= BITS_PER_LONG) {
+				// Single source word
+				*dst = comp(*src << left, *dst, first);
+			} else {
+				// 2 source words
+				d0 = *src++;
+				d1 = *src;
+				*dst = comp(d0 << left | d1 >> right, *dst,
+					    first);
+			}
+		} else {
+			// Multiple destination words
+			d0 = *src++;
+			// Leading bits
+			if (shift > 0) {
+				// Single source word
+				*dst = comp(d0 >> right, *dst, first);
+				dst++;
+				n -= BITS_PER_LONG-dst_idx;
+			} else {
+				// 2 source words
+				d1 = *src++;
+				*dst = comp(d0 << left | d1 >> right, *dst,
+					    first);
+				d0 = d1;
+				dst++;
+				n -= BITS_PER_LONG-dst_idx;
+			}
+
+			// Main chunk
+			m = n % BITS_PER_LONG;
+			n /= BITS_PER_LONG;
+			while (n >= 4) {
+				d1 = *src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				d1 = *src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				d1 = *src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				d1 = *src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				n -= 4;
+			}
+			while (n--) {
+				d1 = *src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+			}
+
+			// Trailing bits
+			if (last) {
+				if (m <= right) {
+					// Single source word
+					*dst = comp(d0 << left, *dst, last);
+				} else {
+					// 2 source words
+					d1 = *src;
+					*dst = comp(d0 << left | d1 >> right,
+						    *dst, last);
+				}
+			}
+		}
+	}
+}
+
+
+    /*
+     *  Unaligned reverse bit copy using 32-bit or 64-bit memory accesses
+     */
+
+static void bitcpy_rev(unsigned long *dst, int dst_idx,
+		       const unsigned long *src, int src_idx, u32 n)
+{
+	unsigned long first, last;
+	int shift = dst_idx-src_idx, left, right;
+	unsigned long d0, d1;
+	int m;
+
+	if (!n)
+		return;
+
+	dst += (n-1)/BITS_PER_LONG;
+	src += (n-1)/BITS_PER_LONG;
+	if ((n-1) % BITS_PER_LONG) {
+		dst_idx += (n-1) % BITS_PER_LONG;
+		dst += dst_idx >> SHIFT_PER_LONG;
+		dst_idx &= BITS_PER_LONG-1;
+		src_idx += (n-1) % BITS_PER_LONG;
+		src += src_idx >> SHIFT_PER_LONG;
+		src_idx &= BITS_PER_LONG-1;
+	}
+
+	shift = dst_idx-src_idx;
+	first = ~0UL << (BITS_PER_LONG-1-dst_idx);
+	last = ~(~0UL << (BITS_PER_LONG-1-((dst_idx-n) % BITS_PER_LONG)));
+
+	if (!shift) {
+		// Same alignment for source and dest
+
+		if ((unsigned long)dst_idx+1 >= n) {
+			// Single word
+			if (last)
+				first &= last;
+			*dst = comp(*src, *dst, first);
+		} else {
+			// Multiple destination words
+			// Leading bits
+			if (first) {
+				*dst = comp(*src, *dst, first);
+				dst--;
+				src--;
+				n -= dst_idx+1;
+			}
+
+			// Main chunk
+			n /= BITS_PER_LONG;
+			while (n >= 8) {
+				*dst-- = *src--;
+				*dst-- = *src--;
+				*dst-- = *src--;
+				*dst-- = *src--;
+				*dst-- = *src--;
+				*dst-- = *src--;
+				*dst-- = *src--;
+				*dst-- = *src--;
+				n -= 8;
+			}
+			while (n--)
+				*dst-- = *src--;
+
+			// Trailing bits
+			if (last)
+				*dst = comp(*src, *dst, last);
+		}
+	} else {
+		// Different alignment for source and dest
+
+		right = shift & (BITS_PER_LONG-1);
+		left = -shift & (BITS_PER_LONG-1);
+
+		if ((unsigned long)dst_idx+1 >= n) {
+			// Single destination word
+			if (last)
+				first &= last;
+			if (shift < 0) {
+				// Single source word
+				*dst = comp(*src << left, *dst, first);
+			} else if (1+(unsigned long)src_idx >= n) {
+				// Single source word
+				*dst = comp(*src >> right, *dst, first);
+			} else {
+				// 2 source words
+				d0 = *src--;
+				d1 = *src;
+				*dst = comp(d0 >> right | d1 << left, *dst,
+					    first);
+			}
+		} else {
+			// Multiple destination words
+			d0 = *src--;
+			// Leading bits
+			if (shift < 0) {
+				// Single source word
+				*dst = comp(d0 << left, *dst, first);
+				dst--;
+				n -= dst_idx+1;
+			} else {
+				// 2 source words
+				d1 = *src--;
+				*dst = comp(d0 >> right | d1 << left, *dst,
+					    first);
+				d0 = d1;
+				dst--;
+				n -= dst_idx+1;
+			}
+
+			// Main chunk
+			m = n % BITS_PER_LONG;
+			n /= BITS_PER_LONG;
+			while (n >= 4) {
+				d1 = *src--;
+				*dst-- = d0 >> right | d1 << left;
+				d0 = d1;
+				d1 = *src--;
+				*dst-- = d0 >> right | d1 << left;
+				d0 = d1;
+				d1 = *src--;
+				*dst-- = d0 >> right | d1 << left;
+				d0 = d1;
+				d1 = *src--;
+				*dst-- = d0 >> right | d1 << left;
+				d0 = d1;
+				n -= 4;
+			}
+			while (n--) {
+				d1 = *src--;
+				*dst-- = d0 >> right | d1 << left;
+				d0 = d1;
+			}
+
+			// Trailing bits
+			if (last) {
+				if (m <= left) {
+					// Single source word
+					*dst = comp(d0 >> right, *dst, last);
+				} else {
+					// 2 source words
+					d1 = *src;
+					*dst = comp(d0 >> right | d1 << left,
+						    *dst, last);
+				}
+			}
+		}
+	}
+}
+
+
+    /*
+     *  Unaligned forward inverting bit copy using 32-bit or 64-bit memory
+     *  accesses
+     */
+
+static void bitcpy_not(unsigned long *dst, int dst_idx,
+		       const unsigned long *src, int src_idx, u32 n)
+{
+	unsigned long first, last;
+	int shift = dst_idx-src_idx, left, right;
+	unsigned long d0, d1;
+	int m;
+
+	if (!n)
+		return;
+
+	shift = dst_idx-src_idx;
+	first = ~0UL >> dst_idx;
+	last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
+
+	if (!shift) {
+		// Same alignment for source and dest
+
+		if (dst_idx+n <= BITS_PER_LONG) {
+			// Single word
+			if (last)
+				first &= last;
+			*dst = comp(~*src, *dst, first);
+		} else {
+			// Multiple destination words
+			// Leading bits
+			if (first) {
+				*dst = comp(~*src, *dst, first);
+				dst++;
+				src++;
+				n -= BITS_PER_LONG-dst_idx;
+			}
+
+			// Main chunk
+			n /= BITS_PER_LONG;
+			while (n >= 8) {
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				*dst++ = ~*src++;
+				n -= 8;
+			}
+			while (n--)
+				*dst++ = ~*src++;
+
+			// Trailing bits
+			if (last)
+				*dst = comp(~*src, *dst, last);
+		}
+	} else {
+		// Different alignment for source and dest
+
+		right = shift & (BITS_PER_LONG-1);
+		left = -shift & (BITS_PER_LONG-1);
+
+		if (dst_idx+n <= BITS_PER_LONG) {
+			// Single destination word
+			if (last)
+				first &= last;
+			if (shift > 0) {
+				// Single source word
+				*dst = comp(~*src >> right, *dst, first);
+			} else if (src_idx+n <= BITS_PER_LONG) {
+				// Single source word
+				*dst = comp(~*src << left, *dst, first);
+			} else {
+				// 2 source words
+				d0 = ~*src++;
+				d1 = ~*src;
+				*dst = comp(d0 << left | d1 >> right, *dst,
+					    first);
+			}
+		} else {
+			// Multiple destination words
+			d0 = ~*src++;
+			// Leading bits
+			if (shift > 0) {
+				// Single source word
+				*dst = comp(d0 >> right, *dst, first);
+				dst++;
+				n -= BITS_PER_LONG-dst_idx;
+			} else {
+				// 2 source words
+				d1 = ~*src++;
+				*dst = comp(d0 << left | d1 >> right, *dst,
+					    first);
+				d0 = d1;
+				dst++;
+				n -= BITS_PER_LONG-dst_idx;
+			}
+
+			// Main chunk
+			m = n % BITS_PER_LONG;
+			n /= BITS_PER_LONG;
+			while (n >= 4) {
+				d1 = ~*src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				d1 = ~*src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				d1 = ~*src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				d1 = ~*src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+				n -= 4;
+			}
+			while (n--) {
+				d1 = ~*src++;
+				*dst++ = d0 << left | d1 >> right;
+				d0 = d1;
+			}
+
+			// Trailing bits
+			if (last) {
+				if (m <= right) {
+					// Single source word
+					*dst = comp(d0 << left, *dst, last);
+				} else {
+					// 2 source words
+					d1 = ~*src;
+					*dst = comp(d0 << left | d1 >> right,
+						    *dst, last);
+				}
+			}
+		}
+	}
+}
+
+
+    /*
+     *  Unaligned 32-bit pattern fill using 32/64-bit memory accesses
+     */
+
+static void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
+{
+	unsigned long val = pat;
+	unsigned long first, last;
+
+	if (!n)
+		return;
+
+#if BITS_PER_LONG == 64
+	val |= val << 32;
+#endif
+
+	first = ~0UL >> dst_idx;
+	last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
+
+	if (dst_idx+n <= BITS_PER_LONG) {
+		// Single word
+		if (last)
+			first &= last;
+		*dst = comp(val, *dst, first);
+	} else {
+		// Multiple destination words
+		// Leading bits
+		if (first) {
+			*dst = comp(val, *dst, first);
+			dst++;
+			n -= BITS_PER_LONG-dst_idx;
+		}
+
+		// Main chunk
+		n /= BITS_PER_LONG;
+		while (n >= 8) {
+			*dst++ = val;
+			*dst++ = val;
+			*dst++ = val;
+			*dst++ = val;
+			*dst++ = val;
+			*dst++ = val;
+			*dst++ = val;
+			*dst++ = val;
+			n -= 8;
+		}
+		while (n--)
+			*dst++ = val;
+
+		// Trailing bits
+		if (last)
+			*dst = comp(val, *dst, last);
+	}
+}
+
+
+    /*
+     *  Unaligned 32-bit pattern xor using 32/64-bit memory accesses
+     */
+
+static void bitxor32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
+{
+	unsigned long val = pat;
+	unsigned long first, last;
+
+	if (!n)
+		return;
+
+#if BITS_PER_LONG == 64
+	val |= val << 32;
+#endif
+
+	first = ~0UL >> dst_idx;
+	last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
+
+	if (dst_idx+n <= BITS_PER_LONG) {
+		// Single word
+		if (last)
+			first &= last;
+		*dst = xor(val, *dst, first);
+	} else {
+		// Multiple destination words
+		// Leading bits
+		if (first) {
+			*dst = xor(val, *dst, first);
+			dst++;
+			n -= BITS_PER_LONG-dst_idx;
+		}
+
+		// Main chunk
+		n /= BITS_PER_LONG;
+		while (n >= 4) {
+			*dst++ ^= val;
+			*dst++ ^= val;
+			*dst++ ^= val;
+			*dst++ ^= val;
+			n -= 4;
+		}
+		while (n--)
+			*dst++ ^= val;
+
+		// Trailing bits
+		if (last)
+			*dst = xor(val, *dst, last);
+	}
+}
+
+static inline void fill_one_line(int bpp, unsigned long next_plane,
+				 unsigned long *dst, int dst_idx, u32 n,
+				 u32 color)
+{
+	while (1) {
+		dst += dst_idx >> SHIFT_PER_LONG;
+		dst_idx &= (BITS_PER_LONG-1);
+		bitfill32(dst, dst_idx, color & 1 ? ~0 : 0, n);
+		if (!--bpp)
+			break;
+		color >>= 1;
+		dst_idx += next_plane*8;
+	}
+}
+
+static inline void xor_one_line(int bpp, unsigned long next_plane,
+				unsigned long *dst, int dst_idx, u32 n,
+				u32 color)
+{
+	while (color) {
+		dst += dst_idx >> SHIFT_PER_LONG;
+		dst_idx &= (BITS_PER_LONG-1);
+		bitxor32(dst, dst_idx, color & 1 ? ~0 : 0, n);
+		if (!--bpp)
+			break;
+		color >>= 1;
+		dst_idx += next_plane*8;
+	}
+}
+
+
+static void atafb_fillrect(struct fb_info *info,
+			   const struct fb_fillrect *rect)
+{
+	struct atafb_par *par = (struct atafb_par *)info->par;
+	int dst_idx, x2, y2;
+	unsigned long *dst;
+	u32 width, height;
+
+	if (!rect->width || !rect->height)
+		return;
+
+	/*
+	 * We could use hardware clipping but on many cards you get around
+	 * hardware clipping by writing to framebuffer directly.
+	 * */
+	x2 = rect->dx + rect->width;
+	y2 = rect->dy + rect->height;
+	x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
+	y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
+	width = x2 - rect->dx;
+	height = y2 - rect->dy;
+
+	atafb_iplan2p8_fillrect(info, par->next_line, rect->color, 
+				rect->dy, rect->dx, height, width);
+}
+
+static inline void copy_one_line(int bpp, unsigned long next_plane,
+				 unsigned long *dst, int dst_idx,
+				 unsigned long *src, int src_idx, u32 n)
+{
+	while (1) {
+		dst += dst_idx >> SHIFT_PER_LONG;
+		dst_idx &= (BITS_PER_LONG-1);
+		src += src_idx >> SHIFT_PER_LONG;
+		src_idx &= (BITS_PER_LONG-1);
+		bitcpy(dst, dst_idx, src, src_idx, n);
+		if (!--bpp)
+			break;
+		dst_idx += next_plane*8;
+		src_idx += next_plane*8;
+	}
+}
+
+static inline void copy_one_line_rev(int bpp, unsigned long next_plane,
+				     unsigned long *dst, int dst_idx,
+				     unsigned long *src, int src_idx, u32 n)
+{
+	while (1) {
+		dst += dst_idx >> SHIFT_PER_LONG;
+		dst_idx &= (BITS_PER_LONG-1);
+		src += src_idx >> SHIFT_PER_LONG;
+		src_idx &= (BITS_PER_LONG-1);
+		bitcpy_rev(dst, dst_idx, src, src_idx, n);
+		if (!--bpp)
+			break;
+		dst_idx += next_plane*8;
+		src_idx += next_plane*8;
+	}
+}
+
+
+static void atafb_copyarea(struct fb_info *info,
+			   const struct fb_copyarea *area)
+{
+	struct atafb_par *par = (struct atafb_par *)info->par;
+	int x2, y2;
+	u32 dx, dy, sx, sy, width, height;
+	unsigned long *dst, *src;
+	int dst_idx, src_idx;
+	int rev_copy = 0;
+
+	/* clip the destination */
+	x2 = area->dx + area->width;
+	y2 = area->dy + area->height;
+	dx = area->dx > 0 ? area->dx : 0;
+	dy = area->dy > 0 ? area->dy : 0;
+	x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
+	y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
+	width = x2 - dx;
+	height = y2 - dy;
+
+	/* update sx,sy */
+	sx = area->sx + (dx - area->dx);
+	sy = area->sy + (dy - area->dy);
+
+	/* the source must be completely inside the virtual screen */
+	if (sx < 0 || sy < 0 || (sx + width) > info->var.xres_virtual ||
+	    (sy + height) > info->var.yres_virtual)
+		return;
+
+	if (dy > sy || (dy == sy && dx > sx)) {
+		dy += height;
+		sy += height;
+		rev_copy = 1;
+	}
+
+	atafb_iplan2p8_copyarea(info, par->next_line, sy, sx, dy, dx, height, width);
+}
+
+
+static inline void expand_one_line(int bpp, unsigned long next_plane,
+				   unsigned long *dst, int dst_idx, u32 n,
+				   const u8 *data, u32 bgcolor, u32 fgcolor)
+{
+    const unsigned long *src;
+    int src_idx;
+
+    while (1) {
+	dst += dst_idx >> SHIFT_PER_LONG;
+	dst_idx &= (BITS_PER_LONG-1);
+	if ((bgcolor ^ fgcolor) & 1) {
+	    src = (unsigned long *)((unsigned long)data & ~(BYTES_PER_LONG-1));
+	    src_idx = ((unsigned long)data & (BYTES_PER_LONG-1))*8;
+	    if (fgcolor & 1)
+		bitcpy(dst, dst_idx, src, src_idx, n);
+	    else
+		bitcpy_not(dst, dst_idx, src, src_idx, n);
+	    /* set or clear */
+	} else
+	    bitfill32(dst, dst_idx, fgcolor & 1 ? ~0 : 0, n);
+	if (!--bpp)
+	    break;
+	bgcolor >>= 1;
+	fgcolor >>= 1;
+	dst_idx += next_plane*8;
+    }
+}
+
+
+static void atafb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct atafb_par *par = (struct atafb_par *)info->par;
+	int x2, y2;
+	unsigned long *dst;
+	int dst_idx;
+	const char *src;
+	u32 dx, dy, width, height, pitch;
+
+	/*
+	 * We could use hardware clipping but on many cards you get around
+	 * hardware clipping by writing to framebuffer directly like we are
+	 * doing here.
+	 */
+	x2 = image->dx + image->width;
+	y2 = image->dy + image->height;
+	dx = image->dx;
+	dy = image->dy;
+	x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
+	y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
+	width  = x2 - dx;
+	height = y2 - dy;
+
+	if (image->depth == 1) {
+		// used for font data
+		dst = (unsigned long *)
+			((unsigned long)info->screen_base & ~(BYTES_PER_LONG-1));
+		dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG-1))*8;
+		dst_idx += dy*par->next_line*8+dx;
+		src = image->data;
+		pitch = (image->width+7)/8;
+		while (height--) {
+
+			atafb_iplan2p8_linefill(info, par->next_line, 
+					dy, dx, width, src, 
+					image->bg_color, image->fg_color);
+			dy++;
+			src += pitch;
+		}
+	} else {
+		// only used for logo
+		c2p(info->screen_base, image->data, dx, dy, width, height,
+		    par->next_line, par->next_plane, image->width,
+		    info->var.bits_per_pixel);
+	}
+}
 static int
 atafb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 {
@@ -2586,7 +3609,7 @@
 		if (copy_from_user((void *)&current_par, (void *)arg,
 				   sizeof(struct atafb_par)))
 			return -EFAULT;
-		atafb_set_par(&current_par);
+		ata_set_par(&current_par);
 		return 0;
 #endif
 	}
@@ -2615,22 +3638,67 @@
 		cmap.transp=NULL;
 		cmap.start=0;
 		cmap.len=16;
-		fb_set_cmap(&cmap, 1, info);
+		fb_set_cmap(&cmap, info);
 	}
+#if 0
 	else
-		do_install_cmap(info->currcon, info);
+		do_install_cmap(info);
+#endif
 	return 0;
 }
 
+	/*
+	 * New fbcon interface ... 
+	 */
+
+	 /* check var by decoding var into hw par, rounding if necessary, 
+	  * then encoding hw par back into new, validated var */
+static int atafb_check_var(struct fb_var_screeninfo *var,
+			   struct fb_info *info)
+{
+	int err;
+	struct atafb_par par;
+
+	/* Validate wanted screen parameters */
+	// if ((err = ata_decode_var(var, &par)))
+	if ((err = fbhw->decode_var(var, &par)))
+		return err;
+
+	/* Encode (possibly rounded) screen parameters */
+	// ata_encode_var(var, &par);
+	fbhw->encode_var(var, &par);
+	return 0;
+}
+
+	/* actually set hw par by decoding var, then setting hardware from 
+	 * hw par just decoded */
+static int atafb_set_par(struct fb_info *info)
+{
+	struct atafb_par *par = (struct atafb_par *)info->par;
+
+	/* Decode wanted screen parameters */
+	// ata_decode_var(&info->var, par);
+	fbhw->decode_var(&info->var, par);
+
+	/* Set new videomode */
+	ata_set_par(par);
+
+	return 0;
+}
+
+
+
+
 static struct fb_ops atafb_ops = {
 	.owner =	THIS_MODULE,
-	.fb_get_fix =	atafb_get_fix,
-	.fb_get_var =	atafb_get_var,
-	.fb_set_var =	atafb_set_var,
-	.fb_get_cmap =	atafb_get_cmap,
-	.fb_set_cmap =	gen_set_cmap,
-	.fb_pan_display =atafb_pan_display,
+	.fb_check_var	= atafb_check_var,
+	.fb_set_par	= atafb_set_par,
+	//.fb_setcolreg	= atafb_setcolreg,
 	.fb_blank =	atafb_blank,
+	.fb_pan_display	= atafb_pan_display,
+	.fb_fillrect	= atafb_fillrect,
+	.fb_copyarea	= atafb_copyarea,
+	.fb_imageblit	= atafb_imageblit,
 	.fb_ioctl =	atafb_ioctl,
 };
 
@@ -2675,17 +3743,19 @@
 		default_mem_req=min_mem;
 }
 
+// Obsolete
 static int
 atafb_switch(int con, struct fb_info *info)
 {
+#if 0
 	/* Do we have to save the colormap ? */
-	if (fb_display[info->currcon].cmap.len)
-		fb_get_cmap(&fb_display[info->currcon].cmap, 1, fbhw->getcolreg,
+	if (info->cmap.len)
+		fb_get_cmap(info->cmap, 1, fbhw->getcolreg,
 			    info);
-	do_fb_set_var(&fb_display[con].var,1);
-	info->currcon=con;
+#endif
+	do_fb_set_var(&info->var,1);
 	/* Install new colormap */
-	do_install_cmap(con, info);
+	// do_install_cmap(info);
 	return 0;
 }
 
@@ -2693,40 +3763,59 @@
 {
 	int pad;
 	int detected_mode;
+	unsigned int defmode;
 	unsigned long mem_req;
 
+#ifndef MODULE
+	char *option = NULL;
+
+	if (fb_get_options("atafb", &option)) {
+		return -ENODEV;
+	}
+	atafb_setup(option);
+#endif
+	printk("atafb_init: start\n");
+
 	if (!MACH_IS_ATARI)
 	        return -ENXIO;
 
 	do {
 #ifdef ATAFB_EXT
 		if (external_addr) {
+			printk("atafb_init: initializing external hw\n");
 			fbhw = &ext_switch;
 			atafb_ops.fb_setcolreg = &ext_setcolreg;
+			defmode = DEFMODE_EXT;
 			break;
 		}
 #endif
 #ifdef ATAFB_TT
 		if (ATARIHW_PRESENT(TT_SHIFTER)) {
+			printk("atafb_init: initializing TT hw\n");
 			fbhw = &tt_switch;
 			atafb_ops.fb_setcolreg = &tt_setcolreg;
+			defmode = DEFMODE_TT;
 			break;
 		}
 #endif
 #ifdef ATAFB_FALCON
 		if (ATARIHW_PRESENT(VIDEL_SHIFTER)) {
+			printk("atafb_init: initializing Falcon hw\n");
 			fbhw = &falcon_switch;
 			atafb_ops.fb_setcolreg = &falcon_setcolreg;
 			request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO,
 			            "framebuffer/modeswitch", falcon_vbl_switcher);
+			defmode = DEFMODE_F30;
 			break;
 		}
 #endif
 #ifdef ATAFB_STE
 		if (ATARIHW_PRESENT(STND_SHIFTER) ||
 		    ATARIHW_PRESENT(EXTD_SHIFTER)) {
+			printk("atafb_init: initializing ST/E hw\n");
 			fbhw = &st_switch;
 			atafb_ops.fb_setcolreg = &stste_setcolreg;
+			defmode = DEFMODE_STE;
 			break;
 		}
 		fbhw = &st_switch;
@@ -2771,6 +3860,8 @@
 			kernel_set_cachemode(screen_base, screen_len,
 					     IOMAP_WRITETHROUGH);
 		}
+		printk("atafb: screen_base %p real_screen_base %p screen_len %ld\n",
+			screen_base, real_screen_base, screen_len);
 #ifdef ATAFB_EXT
 	}
 	else {
@@ -2790,32 +3881,55 @@
 	}
 #endif /* ATAFB_EXT */
 
-	strcpy(fb_info.modename, "Atari Builtin ");
-	fb_info.changevar = NULL;
+//	strcpy(fb_info.mode->name, "Atari Builtin ");
 	fb_info.fbops = &atafb_ops;
-	fb_info.disp = &disp;
-	fb_info.currcon = -1;
-	fb_info.switch_con = &atafb_switch;
-	fb_info.updatevar = &fb_update_var;
-	fb_info.flags = FBINFO_FLAG_DEFAULT;
+	// try to set default var
 	do_fb_set_var(&atafb_predefined[default_par-1], 1);
-	strcat(fb_info.modename, fb_var_names[default_par-1][0]);
+	// reads hw state into current par, which may not be sane yet
+	ata_get_par(&current_par);
+	fb_info.par = &current_par;
+	// tries to read from HW which may not be initialized yet
+	// so set sane var first, then call atafb_set_par
+	atafb_get_var(&current_var, &fb_info);
+	fb_info.var = current_var;
+	fb_info.flags = FBINFO_FLAG_DEFAULT;
 
-	atafb_get_var(&disp.var, -1, &fb_info);
-	atafb_set_disp(-1, &fb_info);
-	do_install_cmap(0, &fb_info);
+	printk("atafb_init: info %p par %p var %p\n", 
+		&fb_info, fb_info.par, &(fb_info.var));
 
-	if (register_framebuffer(&fb_info) < 0)
+	if (!fb_find_mode(&fb_info.var, &fb_info, mode_option, atafb_modedb,
+			  NUM_TOTAL_MODES, &atafb_modedb[defmode], 4)) {
 		return -EINVAL;
+	}
+
+	fb_alloc_cmap(&(fb_info.cmap), 256, 0);
+
+//	do_fb_set_var(&atafb_predefined[default_par-1], 1);
+// 	strcat(fb_info.mode->name, fb_var_names[default_par-1][0]);
+
+	atafb_set_disp(&fb_info);
+	// do_install_cmap(&fb_info);
+
+	printk("atafb_init: info %p par %p var %p\n", 
+		&fb_info, fb_info.par, &(fb_info.var));
 
 	printk("Determined %dx%d, depth %d\n",
-	       disp.var.xres, disp.var.yres, disp.var.bits_per_pixel);
-	if ((disp.var.xres != disp.var.xres_virtual) ||
-	    (disp.var.yres != disp.var.yres_virtual))
+	       current_var.xres, current_var.yres, current_var.bits_per_pixel);
+	if ((current_var.xres != current_var.xres_virtual) ||
+	    (current_var.yres != current_var.yres_virtual))
 	   printk("   virtual %dx%d\n",
-			  disp.var.xres_virtual, disp.var.yres_virtual);
-	printk("fb%d: %s frame buffer device, using %dK of video memory\n",
-	       fb_info.node, fb_info.modename, screen_len>>10);
+			  current_var.xres_virtual, current_var.yres_virtual);
+
+	printk("atafb_init: before register_framebuffer, info %p\n", &fb_info);
+	if (register_framebuffer(&fb_info) < 0)
+		return -EINVAL;
+	printk("atafb_init: after register_framebuffer, info %p\n", &fb_info);
+
+	// FIXME: mode needs setting!
+	//printk("fb%d: %s frame buffer device, using %dK of video memory\n",
+	//       fb_info.node, fb_info.mode->name, screen_len>>10);
+	printk("fb%d: frame buffer device, using %dK of video memory\n",
+	       fb_info.node, screen_len>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	return 0;
@@ -3039,19 +4153,16 @@
     char *this_opt;
     int temp;
 
-    fb_info.fontname[0] = '\0';
-
     if (!options || !*options)
 		return 0;
     
     while ((this_opt = strsep(&options, ",")) != NULL) {	 
 	if (!*this_opt) continue;
-	if ((temp=get_video_mode(this_opt)))
+	if ((temp=get_video_mode(this_opt))) {
 		default_par=temp;
-	else if (! strcmp(this_opt, "inverse"))
+		mode_option = this_opt;
+	} else if (! strcmp(this_opt, "inverse"))
 		inverse=1;
-	else if (!strncmp(this_opt, "font:", 5))
-	   strcpy(fb_info.fontname, this_opt+5);
 	else if (! strncmp(this_opt, "hwscroll_",9)) {
 		hwscroll=simple_strtoul(this_opt+9, NULL, 10);
 		if (hwscroll < 0)
@@ -3087,11 +4198,14 @@
     return 0;
 }
 
+module_init(atafb_init);
+
 #ifdef MODULE
 MODULE_LICENSE("GPL");
 
-int init_module(void)
+int cleanup_module(void)
 {
-	return atafb_init();
+	unregister_framebuffer(&fb_info);
+	return atafb_deinit();
 }
 #endif /* MODULE */
--- linux-2.6.17-m68k-vanilla/drivers/video/Kconfig	2006-06-18 17:08:33.000000000 +0200
+++ linux-2.6.17-m68k/drivers/video/Kconfig	2006-09-13 16:34:15.000000000 +0200
@@ -335,7 +335,10 @@
 
 config FB_ATARI
 	bool "Atari native chipset support"
-	depends on (FB = y) && ATARI && BROKEN
+	depends on (FB = y) && ATARI
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
 	help
 	  This is the frame buffer device driver for the builtin graphics
 	  chipset found in Ataris.
--- linux-2.6.17-m68k-vanilla/drivers/video/Makefile	2006-09-13 16:41:01.000000000 +0200
+++ linux-2.6.17-m68k/drivers/video/Makefile	2006-09-22 16:22:38.000000000 +0200
@@ -62,7 +62,7 @@
 obj-$(CONFIG_FB_LEO)              += leo.o sbuslib.o
 obj-$(CONFIG_FB_SGIVW)            += sgivwfb.o
 obj-$(CONFIG_FB_ACORN)            += acornfb.o
-obj-$(CONFIG_FB_ATARI)            += atafb.o
+obj-$(CONFIG_FB_ATARI)            += atafb.o c2p.o atafb_iplan2p8.o
 obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HGA)              += hgafb.o
 obj-$(CONFIG_FB_IGA)              += igafb.o
--- linux-2.6.17-m68k-vanilla/drivers/video/atafb_iplan2p8.c	2006-09-22 18:09:21.434236689 +0200
+++ linux-2.6.17-m68k/drivers/video/atafb_iplan2p8.c	2006-09-22 18:11:40.449281567 +0200
@@ -0,0 +1,412 @@
+/*
+ *  linux/drivers/video/iplan2p8.c -- Low level frame buffer operations for
+ *				      interleaved bitplanes à la Atari (8
+ *				      planes, 2 bytes interleave)
+ *
+ *	Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+
+#include <asm/byteorder.h>
+
+#ifdef __mc68000__
+#include <asm/setup.h>
+#endif
+
+#include "console/fbcon.h"
+#include "atafb_iplan2p8.h"
+
+
+    /*
+     *  Interleaved bitplanes à la Atari (8 planes, 2 bytes interleave)
+     *
+     *  In 8 plane mode, 256 colors would be possible, but only the first
+     *  16 are used by the console code (the upper 4 bits are
+     *  background/unused). For that, the following functions mask off the
+     *  higher 4 bits of each color.
+     */
+
+/* Increment/decrement 8 plane addresses */
+
+#define	INC_8P(p)	do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0)
+#define	DEC_8P(p)	do { if ((long)(--(p)) & 1) (p) -= 12; } while(0)
+
+/* This seems to work best with the new fbcon driver */
+#define	INC_8P2(p)	do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0)
+
+/* Perform the m68k movepl operation extended to 64 bits.  */
+static inline void movepl2(u8 *d, u32 val1, u32 val2)
+{
+#if defined __mc68000__ && !defined CPU_M68060_ONLY
+    asm volatile ("movepl %1,%0@(0); movepl %2,%0@(8)"
+		  : : "a" (d), "d" (val1), "d" (val2));
+#else
+    d[0] = (val1 >> 24) & 0xff;
+    d[2] = (val1 >> 16) & 0xff;
+    d[4] = (val1 >> 8) & 0xff;
+    d[6] = val1 & 0xff;
+    d[8] = (val2 >> 24) & 0xff;
+    d[10] = (val2 >> 16) & 0xff;
+    d[12] = (val2 >> 8) & 0xff;
+    d[14] = val2 & 0xff;
+#endif
+}
+
+/* Sets the bytes in the visible column at d, height h, to the value
+ * val1,val2 for a 8 plane screen. The bits of the color in 'color' are
+ * moved (8 times) to the respective bytes. This means:
+ *
+ * for(h times; d += bpr)
+ *   *d      = (color & 1) ? 0xff : 0;
+ *   *(d+2)  = (color & 2) ? 0xff : 0;
+ *   *(d+4)  = (color & 4) ? 0xff : 0;
+ *   *(d+6)  = (color & 8) ? 0xff : 0;
+ *   *(d+8)  = (color & 16) ? 0xff : 0;
+ *   *(d+10) = (color & 32) ? 0xff : 0;
+ *   *(d+12) = (color & 64) ? 0xff : 0;
+ *   *(d+14) = (color & 128) ? 0xff : 0;
+ */
+
+static __inline__ void memclear_8p_col(void *d, size_t h, u32 val1,
+                                       u32 val2, int bpr)
+{
+    u8 *dd = d;
+    do {
+	movepl2(dd, val1, val2);
+	dd += bpr;
+    } while (--h);
+}
+
+/* Sets a 8 plane region from 'd', length 'count' bytes, to the color
+ * val1..val4. 'd' has to be an even address and count must be divisible
+ * by 16, because only whole words and all planes are accessed. I.e.:
+ *
+ * for(count/16 times)
+ *   *d      = *(d+1)  = (color & 1) ? 0xff : 0;
+ *   *(d+2)  = *(d+3)  = (color & 2) ? 0xff : 0;
+ *   *(d+4)  = *(d+5)  = (color & 4) ? 0xff : 0;
+ *   *(d+6)  = *(d+7)  = (color & 8) ? 0xff : 0;
+ *   *(d+8)  = *(d+9)  = (color & 16) ? 0xff : 0;
+ *   *(d+10) = *(d+11) = (color & 32) ? 0xff : 0;
+ *   *(d+12) = *(d+13) = (color & 64) ? 0xff : 0;
+ *   *(d+14) = *(d+15) = (color & 128) ? 0xff : 0;
+ */
+
+static __inline__ void memset_even_8p(void *d, size_t count, u32 val1,
+                                      u32 val2, u32 val3, u32 val4)
+{
+    u32 *dd = d;
+
+    count /= 16;
+    while (count--) {
+	*dd++ = val1;
+	*dd++ = val2;
+	*dd++ = val3;
+	*dd++ = val4;
+    }
+}
+
+/* Copies a 8 plane column from 's', height 'h', to 'd'. */
+
+static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr)
+{
+    u8 *dd = d, *ss = s;
+
+    while (h--) {
+	dd[0] = ss[0];
+	dd[2] = ss[2];
+	dd[4] = ss[4];
+	dd[6] = ss[6];
+	dd[8] = ss[8];
+	dd[10] = ss[10];
+	dd[12] = ss[12];
+	dd[14] = ss[14];
+	dd += bpr;
+	ss += bpr;
+    }
+}
+
+
+/* This expands a 8 bit color into two longs for two movepl (8 plane)
+ * operations.
+ */
+
+static const u32 four2long[] =
+{
+    0x00000000, 0xff000000, 0x00ff0000, 0xffff0000,
+    0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00,
+    0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff,
+    0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff,
+};
+
+static __inline__ void expand8dl(u8 c, u32 *ret1, u32 *ret2)
+{
+    *ret1 = four2long[c & 15];
+    *ret2 = four2long[c >> 4];
+}
+
+
+/* This expands a 8 bit color into four longs for four movel operations
+ * (8 planes).
+ */
+
+static const u32 two2word[] =
+{
+#ifndef __LITTLE_ENDIAN
+    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#endif
+};
+
+static inline void expand8ql(u8 c, u32 *rv1, u32 *rv2, u32 *rv3, u32 *rv4)
+{
+    *rv1 = two2word[c & 4];
+    *rv2 = two2word[(c >> 2) & 4];
+    *rv3 = two2word[(c >> 4) & 4];
+    *rv4 = two2word[c >> 6];
+}
+
+
+/* This duplicates a byte 4 times into a long. */
+
+static __inline__ u32 dup4l(u8 c)
+{
+    u32 rv;
+
+    rv = c;
+    rv |= rv << 8;
+    rv |= rv << 16;
+    return rv;
+}
+
+
+void atafb_iplan2p8_copyarea(struct fb_info *info, u_long next_line,
+                             int sy, int sx, int dy, int dx,
+			     int height, int width)
+{
+    /*  bmove() has to distinguish two major cases: If both, source and
+     *  destination, start at even addresses or both are at odd
+     *  addresses, just the first odd and last even column (if present)
+     *  require special treatment (memmove_col()). The rest between
+     *  then can be copied by normal operations, because all adjacent
+     *  bytes are affected and are to be stored in the same order.
+     *    The pathological case is when the move should go from an odd
+     *  address to an even or vice versa. Since the bytes in the plane
+     *  words must be assembled in new order, it seems wisest to make
+     *  all movements by memmove_col().
+     */
+
+     if (sx == 0 && dx == 0 && width * 8 == next_line) {
+	/*  Special (but often used) case: Moving whole lines can be
+	 *  done with memmove()
+	 */
+	memmove((u8 *)info->screen_base + dy * next_line,
+                (u8 *)info->screen_base + sy * next_line,
+                next_line * height);
+     } else {
+	int rows, cols;
+	u8 *src;
+	u8 *dst;
+	int bytes = next_line;
+	int linesize;
+	u_int colsize;
+	u_int upwards = (dy < sy) || (dy == sy && dx < sx);
+
+        linesize = bytes;
+        colsize = height;
+	if ((sx & 1) == (dx & 1)) {
+	    /* odd->odd or even->even */
+
+	    if (upwards) {
+		src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+		dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+		if (sx & 1) {
+		    memmove_8p_col(dst, src, colsize, bytes);
+		    src += 15;
+		    dst += 15;
+		    --width;
+		}
+		if (width > 1) {
+		    for(rows = colsize; rows > 0; --rows) {
+			memmove (dst, src, (width >> 1) * 16);
+			src += bytes;
+			dst += bytes;
+		    }
+		}
+
+		if (width & 1) {
+		    src -= colsize * bytes;
+		    dst -= colsize * bytes;
+		    memmove_8p_col(dst + (width>>1)*16, src + (width>>1)*16,
+		    colsize, bytes);
+		}
+	    } else {
+		if (!((sx+width-1) & 1)) {
+		    src = (u8 *)info->screen_base + sy * linesize + ((sx+width-1)>>1)*16;
+		    dst = (u8 *)info->screen_base + dy * linesize + ((dx+width-1)>>1)*16;
+		    memmove_8p_col(dst, src, colsize, bytes);
+		    --width;
+		}
+		src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+		dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+		if (width > 1) {
+		    src += colsize * bytes + (sx & 1)*15;
+		    dst += colsize * bytes + (sx & 1)*15;
+		    for(rows = colsize; rows > 0; --rows) {
+			src -= bytes;
+			dst -= bytes;
+			memmove (dst, src, (width>>1)*16);
+		    }
+		}
+		if (width & 1)
+		    memmove_8p_col(dst-15, src-15, colsize, bytes);
+	    }
+	} else {
+	/* odd->even or even->odd */
+
+	    if (upwards) {
+		src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+		dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+		for(cols = width; cols > 0; --cols) {
+		    memmove_8p_col(dst, src, colsize, bytes);
+		    INC_8P(src);
+		    INC_8P(dst);
+		}
+	    } else {
+		sx += width-1;
+		dx += width-1;
+		src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+		dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+		for(cols = width; cols > 0; --cols) {
+		    memmove_8p_col(dst, src, colsize, bytes);
+		    DEC_8P(src);
+		    DEC_8P(dst);
+		}
+	    }
+	}
+    }
+}
+
+void atafb_iplan2p8_fillrect(struct fb_info *info, u_long next_line, u32 color, 
+                             int sy, int sx, int height, int width)
+{
+    u32 offset;
+    u8 *start;
+    int rows;
+    int bytes = next_line;
+    int lines;
+    u32 size;
+    u32 cval1, cval2, cval3, cval4, pcval1, pcval2;
+
+    //if (sx != 0)
+    //  printk("fillrect: sy %d sx %d color %d h %d w %d\n", 
+    //        sy, sx, color, height, width);
+
+    expand8ql(color, &cval1, &cval2, &cval3, &cval4);
+
+    lines = height;
+
+    if (sx == 0 && width * 8 == bytes) {
+        offset = (sy * bytes);
+	size    = lines * bytes;
+	memset_even_8p((u8 *)info->screen_base + offset, size, cval1, cval2, cval3, cval4);
+    } else {
+        offset = sy * bytes + sx;
+	start = (u8 *) info->screen_base + offset;
+	expand8dl(color, &pcval1, &pcval2);
+
+	/* Clears are split if the region starts at an odd column or
+	* end at an even column. These extra columns are spread
+	* across the interleaved planes. All in between can be
+	* cleared by normal fb_memclear_small(), because both bytes of
+	* the single plane words are affected.
+	*/
+
+	if (sx & 1) {
+	    memclear_8p_col(start, lines, pcval1, pcval2, bytes);
+	    start += 7;
+	    width--;
+	}
+	if (width & 1) {
+	    memclear_8p_col(start + (width>>1)*2, lines, pcval1,
+	    pcval2, bytes);
+	    width--;
+	}
+	if (width)
+	    for(rows = lines; rows-- ; start += bytes)
+		memset_even_8p(start, width/8, cval1, cval2, cval3, cval4);
+	}
+}
+
+void atafb_iplan2p8_linefill(struct fb_info *info, u_long next_line,
+                             int dy, int dx, u32 width,
+                             const u8 *data, u32 bgcolor, u32 fgcolor)
+{
+    u8 *dest;
+    u8 *cdat;
+    int rows, start, curr;
+    int bytes = next_line;
+    u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
+
+    cdat = data;
+
+    // careful - proper alignment required, or color smearing!
+    // dest = (u8*) info->screen_base + dy * bytes + (dx>>1) + (dx & 2);
+    // maybe INC_8P2(dest) instead?
+    dest = (u8*) info->screen_base + dy * bytes + (dx>>1);
+    if ((dx>>1) & 0xff)
+      INC_8P2(dest);
+
+    start = dx;
+
+    expand8dl(fgcolor, &fgx1, &fgx2);
+    expand8dl(bgcolor, &bgx1, &bgx2);
+    eorx1 = fgx1 ^ bgx1; eorx2  = fgx2 ^ bgx2;
+
+    if (1 || (bgcolor ^ fgcolor) & 1) {
+      for(rows = width/8, curr = start ; rows-- ; curr++ /* check margin*/) {
+        if (curr > bytes*2) {
+          continue;
+        }
+	fdx = dup4l(*cdat++);
+	movepl2(dest, (fdx & eorx1) ^ bgx1, (fdx & eorx2) ^ bgx2);
+	INC_8P2(dest);
+      }
+    } else {
+      u32 cval1, cval2, cval3, cval4;
+      expand8ql(fgcolor, &cval1, &cval2, &cval3, &cval4);
+      memset_even_8p(dest, width*8, cval1, cval2, cval3, cval4);
+    }
+    
+}
+
+#ifdef MODULE
+MODULE_LICENSE("GPL");
+
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(atafb_iplan2p8_copyarea);
+EXPORT_SYMBOL(atafb_iplan2p8_fillrect);
+EXPORT_SYMBOL(atafb_iplan2p8_linefill);
--- linux-2.6.17-m68k-vanilla/drivers/video/atafb_iplan2p8.h	2006-09-22 18:35:06.413620559 +0200
+++ linux-2.6.17-m68k/drivers/video/atafb_iplan2p8.h	2006-09-22 16:20:45.000000000 +0200
@@ -0,0 +1,29 @@
+/*
+ *  FBcon low-level driver for Atari interleaved bitplanes (8 planes) (iplan2p8)
+ */
+
+#ifndef _VIDEO_FBCON_IPLAN2P8_H
+#define _VIDEO_FBCON_IPLAN2P8_H
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#if defined(CONFIG_FBCON_IPLAN2P8) || defined(CONFIG_FBCON_IPLAN2P8_MODULE)
+#define FBCON_HAS_IPLAN2P8
+#endif
+#else
+#if defined(CONFIG_FBCON_IPLAN2P8)
+#define FBCON_HAS_IPLAN2P8
+#endif
+#endif
+
+extern void atafb_iplan2p8_copyarea(struct fb_info *info, u_long next_line, int sy, int sx, int dy,
+				 int dx, int height, int width);
+extern void atafb_iplan2p8_fillrect(struct fb_info *info, u_long next_line, u32 color,
+				 int sy, int sx, int height, int width);
+extern void atafb_iplan2p8_linefill(struct fb_info *info, u_long next_line,
+                             int dy, int dx, u32 width,
+                             const u8 *data, u32 bgcolor, u32 fgcolor);
+
+
+#endif /* _VIDEO_FBCON_IPLAN2P8_H */

Reply to: