Psion framebuffer driver patch for linux/drivers/video/ files

This patch contains updates to the following files:
./drivers/video/Config.in
./drivers/video/Makefile
./drivers/video/fbmem.c
./drivers/video/psionwfb.c
./drivers/video/psionwfb.h


diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/Config.in linux-2.4.19-rmk2/drivers/video/Config.in
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/Config.in linux-2.4.19-rmk2/drivers/video/Config.in
--- /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/Config.in	2002-10-02 22:05:15.000000000 -0700
+++ linux-2.4.19-rmk2/drivers/video/Config.in	2002-10-02 22:07:30.000000000 -0700
@@ -34,6 +34,7 @@
      dep_bool '  Acorn VIDC support' CONFIG_FB_ACORN $CONFIG_ARCH_ACORN
      dep_bool '  Anakin LCD support' CONFIG_FB_ANAKIN $CONFIG_ARCH_ANAKIN
      dep_bool '  CLPS711X LCD support' CONFIG_FB_CLPS711X $CONFIG_ARCH_CLPS711X
+     dep_bool '  Psion LCD support' CONFIG_FB_PSIONW $CONFIG_ARCH_PSIONW
      dep_bool '  SA-1100 LCD support' CONFIG_FB_SA1100 $CONFIG_ARCH_SA1100
      if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF" = "y" ]; then
         choice 'CerfBoard LCD Display Size' \
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/Makefile linux-2.4.19-rmk2/drivers/video/Makefile
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/Makefile linux-2.4.19-rmk2/drivers/video/Makefile
--- /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/Makefile	2002-10-02 22:05:15.000000000 -0700
+++ linux-2.4.19-rmk2/drivers/video/Makefile	2002-10-02 22:07:30.000000000 -0700
@@ -57,6 +57,7 @@
 obj-$(CONFIG_FB_VALKYRIE)         += valkyriefb.o
 obj-$(CONFIG_FB_CT65550)          += chipsfb.o
 obj-$(CONFIG_FB_CLPS711X)         += clps711xfb.o
+obj-$(CONFIG_FB_PSIONW)           += psionwfb.o
 obj-$(CONFIG_FB_CYBER)            += cyberfb.o
 obj-$(CONFIG_FB_CYBER2000)        += cyber2000fb.o
 obj-$(CONFIG_FB_SGIVW)            += sgivwfb.o
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/fbmem.c linux-2.4.19-rmk2/drivers/video/fbmem.c
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/fbmem.c linux-2.4.19-rmk2/drivers/video/fbmem.c
--- /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/fbmem.c	2002-10-02 22:05:15.000000000 -0700
+++ linux-2.4.19-rmk2/drivers/video/fbmem.c	2002-10-02 22:07:30.000000000 -0700
@@ -62,6 +62,7 @@
 extern int pm3fb_init(void);
 extern int pm3fb_setup(char*);
 extern int clps711xfb_init(void);
+extern int psionwfb_init(void);
 extern int cyber2000fb_init(void);
 extern int cyber2000fb_setup(char*);
 extern int retz3fb_init(void);
@@ -167,6 +168,9 @@
 #ifdef CONFIG_FB_CLPS711X
 	{ "clps711xfb", clps711xfb_init, NULL },
 #endif
+#ifdef CONFIG_FB_PSIONW
+        { "psionwfb", psionwfb_init, NULL },
+#endif
 #ifdef CONFIG_FB_CYBER
 	{ "cyber", cyberfb_init, cyberfb_setup },
 #endif
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/psionwfb.c linux-2.4.19-rmk2/drivers/video/psionwfb.c
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/psionwfb.c linux-2.4.19-rmk2/drivers/video/psionwfb.c
--- /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/psionwfb.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.19-rmk2/drivers/video/psionwfb.c	2002-10-02 22:07:30.000000000 -0700
@@ -0,0 +1,942 @@
+/*
+ *  linux/drivers/video/psionwfb.c
+ *  
+ *  Copyright (C) 2001 Shane Nay <shane@place.org>
+ *  Copyright (C) 2001 Tony Lindgren <tony@atomide.com>
+ * 
+ *  Based on the EP7212 driver, some portions of code  
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/ioport.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-mfb.h>
+#include <video/fbcon-cfb2.h>
+#include <video/fbcon-cfb4.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#include <asm/hardware/psionw.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/psionw-power.h>
+
+#include "psionwfb.h"
+
+/*
+ *    Set a single color register. Return != 0 for invalid regno.
+ */
+static int
+psionwfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+		   u_int transp, struct fb_info *info)
+{
+	unsigned int level, val, previous;
+	int bpp = info->var.bits_per_pixel;
+
+	/* 
+	 * These are the colormaps Epoc uses. By default Linux
+	 * produces white on black text. Inverting everything
+	 * would invert the graphics too, AFAIK. So we our own
+	 * text-only inversion by overriding the key text output
+	 * functions. Maybe there is some way of doing all this
+	 * already in the fb support? Let me know if there's an
+	 * easier way to do this.
+	 */
+	int pal_1bpp[] = { 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+	};
+
+	int pal_2bpp[] = { 0xf, 0xa, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+	};
+
+	int pal_4bpp[] = { 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7,
+		0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0
+	};
+
+	if (regno >= CMAP_SIZE)
+		return 1;
+
+	/*
+	 * FIXME5MX: The graylevel calculation does not quite 
+	 * produce the right colors. Deal with this later.
+	 */
+
+	/* gray = 0.30*R + 0.58*G + 0.11*B */
+	//level = (red * 77 + green * 151 + blue * 28) >> 20;
+
+	/* 
+	 * We need to invert the colors to produce Epoc like
+	 * colormap. Otherwise the logo colors get inverted.
+	 */
+	level = (15 - level);
+
+	switch (bpp) {
+	case 1:
+		/* Just use a fixed map */
+		val = pal_1bpp[regno];
+		break;
+	case 2:
+		/* Just use a fixed map */
+		val = pal_2bpp[regno];
+		break;
+	default:
+		/* Use the calculated level */
+		//val = level;
+		/* Use a fixed map */
+		val = pal_4bpp[regno];
+	}
+
+	// The first entry in the palette contains the bpp value
+	if (regno == 0) {
+		val |= ((bpp >> 1) << 12);
+	}
+	// The LCD controller registers are 16-bit wide
+	if (regno & 0x1) {
+		previous = *(volatile uint *) (LCD_PAL_START + (regno - 1) * 2);
+		val = (val << 16);
+		val = val | previous;
+		*(volatile uint *) (LCD_PAL_START + (regno - 1) * 2) = val;
+	} else {
+		*(volatile uint *) (LCD_PAL_START + regno * 2) = val;
+	}
+
+#ifdef DEBUG
+	if (regno == 0xf) {
+		/* Dump the color palette to serial terminal */
+		dump_pal();
+	}
+#endif
+
+	return 0;
+}
+
+/*
+ * Set the colormap
+ */
+static int
+psionwfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	struct psionwfb_info *cfb = (struct psionwfb_info *) info;
+	struct fb_cmap *dcmap = &fb_display[con].cmap;
+	int err = 0;
+
+	/* no colormap allocated? */
+
+	if (!dcmap->len)
+		err = fb_alloc_cmap(dcmap, CMAP_SIZE, 0);
+
+	if (!err && con == cfb->currcon) {
+		err = fb_set_cmap(cmap, kspc, psionwfb_setcolreg, &cfb->fb);
+		dcmap = &cfb->fb.cmap;
+	}
+
+	if (!err)
+		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
+
+	return err;
+}
+
+/*
+ *    Set the User Defined Part of the Display
+ */
+static int
+psionwfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	struct display *display;
+	int chgvar = 0;
+	int syscon = 0, lcdcon = 0;
+
+	if (var->activate & FB_ACTIVATE_TEST)
+		return 0;
+
+	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
+		return -EINVAL;
+
+	if (cfb->fb.var.xres != var->xres)
+		chgvar = 1;
+	if (cfb->fb.var.yres != var->yres)
+		chgvar = 1;
+	if (cfb->fb.var.xres_virtual != var->xres_virtual)
+		chgvar = 1;
+	if (cfb->fb.var.yres_virtual != var->yres_virtual)
+		chgvar = 1;
+	if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
+		chgvar = 1;
+
+	if (con < 0) {
+		display = cfb->fb.disp;
+		chgvar = 0;
+	} else {
+		display = fb_display + con;
+	}
+
+	var->transp.msb_right = 0;
+	var->transp.offset = 0;
+	var->transp.length = 0;
+	var->red.msb_right = 0;
+	var->red.offset = 0;
+	var->red.length = var->bits_per_pixel;
+	var->green = var->red;
+	var->blue = var->red;
+
+	switch (var->bits_per_pixel) {
+#ifdef FBCON_HAS_MFB
+	case 1:
+		cfb->fb.fix.visual = FB_VISUAL_MONO01;
+		display->dispsw = &fbcon_mfb_psionw;	/* We override */
+		display->dispsw_data = NULL;
+		display->inverse = 0;	/* We do our own text-only inversion */
+		break;
+#endif
+#ifdef FBCON_HAS_CFB2
+	case 2:
+		cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+		display->dispsw = &fbcon_cfb2_psionw;	/* We override */
+		display->dispsw_data = NULL;
+		display->inverse = 0;	/* We do our own text-only inversion */
+		break;
+#endif
+#ifdef FBCON_HAS_CFB4
+	case 4:
+		cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+		display->dispsw = &fbcon_cfb4_psionw;	/* We override */
+		display->dispsw_data = NULL;
+		display->inverse = 0;	/* We do our own text-only inversion */
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
+	display->screen_base = cfb->fb.screen_base;
+	display->line_length = var->xres * var->bits_per_pixel / 8;
+	display->next_line = display->line_length;
+	cfb->fb.fix.line_length = display->line_length;
+
+	display->visual = cfb->fb.fix.visual;
+	display->type = cfb->fb.fix.type;
+	display->type_aux = cfb->fb.fix.type_aux;
+	display->ypanstep = cfb->fb.fix.ypanstep;
+	display->ywrapstep = cfb->fb.fix.ywrapstep;
+	display->can_soft_blank = 1;
+	display->inverse = 0;
+
+	cfb->fb.var = *var;
+	cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
+
+	/*
+	 * Update the old var.  The fbcon drivers still use this.
+	 * Once they are using cfb->fb.var, this can be dropped.
+	 *                                      --rmk
+	 */
+	display->var = cfb->fb.var;
+
+	/*
+	 * If we are setting all the virtual consoles, also set the
+	 * defaults used to create new consoles.
+	 */
+	if (var->activate & FB_ACTIVATE_ALL)
+		cfb->fb.disp->var = cfb->fb.var;
+
+	if (chgvar && info && cfb->fb.changevar)
+		cfb->fb.changevar(con);
+
+	psionwfb_set_timings(var->xres, var->yres, var->pixclock);
+
+	fb_set_cmap(&cfb->fb.cmap, 1, psionwfb_setcolreg, &cfb->fb);
+
+	return 0;
+}
+
+/*
+ * Get the currently displayed virtual consoles colormap.
+ */
+static int
+gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
+	return 0;
+}
+
+/*
+ * Get the currently displayed virtual consoles fixed part of the display.
+ */
+static int
+gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+{
+	*fix = info->fix;
+	return 0;
+}
+
+/*
+ * Get the current user defined part of the display.
+ */
+static int
+gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	*var = info->var;
+	return 0;
+}
+
+static struct fb_ops psionwfb_ops = {
+	owner:THIS_MODULE,
+	fb_set_var:psionwfb_set_var,
+	fb_set_cmap:psionwfb_set_cmap,
+	fb_get_fix:gen_get_fix,
+	fb_get_var:gen_get_var,
+	fb_get_cmap:gen_get_cmap,
+};
+
+static int
+psionwfb_switch(int con, struct fb_info *info)
+{
+	struct psionwfb_info *cfb = (struct psionwfb_info *) info;
+	struct display *disp;
+	struct fb_cmap *cmap;
+
+	if (cfb->currcon >= 0) {
+		disp = fb_display + cfb->currcon;
+
+		/*
+		 * Save the old colormap and video mode.
+		 */
+
+		disp->var = cfb->fb.var;
+		if (disp->cmap.len)
+			fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
+	}
+
+	cfb->currcon = con;
+	disp = fb_display + con;
+
+	/*
+	 * Install the new colormap and change the video mode.  By default,
+	 * fbcon sets all the colormaps and video modes to the default
+	 * values at bootup.
+	 */
+
+	if (disp->cmap.len)
+		cmap = &disp->cmap;
+	else
+		cmap = fb_default_cmap(CMAP_SIZE);
+
+	fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
+
+	cfb->fb.var = disp->var;
+	cfb->fb.var.activate = FB_ACTIVATE_NOW;
+
+	psionwfb_set_var(&cfb->fb.var, con, &cfb->fb);
+
+	return 0;
+}
+
+static int
+psionwfb_updatevar(int con, struct fb_info *info)
+{
+	return -EINVAL;
+}
+
+static void
+psionwfb_blank(int blank, struct fb_info *info)
+{
+	if (blank) {
+		psionw_lcd_powerdown(1);
+	} else {
+		psionw_lcd_powerup(1);
+	}
+}
+
+static inline int get_pcd(unsigned int pixclock)
+{
+	unsigned int pcd;
+	if (pixclock) {
+		pcd = psionw_get_cpu_speed(0) / 100;
+		pcd *= pixclock;
+		pcd /= 10000000;
+		pcd += 1;	/* make up for integer math truncations */		
+	} else {
+		printk(KERN_WARNING "Zero pixclock detected\n");
+	}
+	return pcd;
+}
+
+/*
+ * LCD Interrupt handler
+ */
+static void
+psionwfb_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int lcderr;
+
+	//printk("LCD interrupt\n");
+
+	lcderr = psionw_readl(LCDCTL);
+	lcderr &= ~LCDCTL_DONE;	// Clear bit 3, done mask
+	lcderr &= ~LCDCTL_NEXT;	// Clear bit 4, next mask
+	lcderr &= ~LCDCTL_ERR;	// Clear bit 5, error mask
+	psionw_writel(lcderr, LCDCTL);
+	lcderr = psionw_readl(LCDST);
+	lcderr |= LCDST_NEXT;	// Set next frame
+	lcderr &= ~LCDST_BER;	// Clear bus error
+	lcderr &= ~LCDST_ABC;	// Clear AC bias
+	lcderr &= ~LCDST_FUF;	// Clear FIFO underflow
+	psionw_writel(lcderr, LCDST);
+}
+
+
+int __init
+psionwfb_init(void)
+{
+	int err = -ENOMEM, ret = 0;
+
+	cfb = kmalloc(sizeof (*cfb) + sizeof (struct display), GFP_KERNEL);
+	if (!cfb)
+		goto out;
+
+	memset(cfb, 0, sizeof (*cfb) + sizeof (struct display));
+
+	/* 
+	 * See also arm/arch/mm/init.c to poke a hole to the bootmem.
+	 * This request_mem_region call does not add the data 
+	 * currently to the /proc/iomem, but it may later on.
+	 */
+	//request_mem_region(LCD_MEM_START, LCD_MEM_SIZE, "psionwfb");
+	request_region(LCD_MEM_START, LCD_MEM_SIZE, "psionwfb");
+
+	memset((void *) LCD_PAL_START, 0, LCD_PAL_SIZE);
+	memset((void *) LCD_BUF_START, 0, LCD_BUF_SIZE);
+
+	cfb->fb.screen_base = (void *) (LCD_BUF_START);
+	cfb->fb.fix.smem_start = LCD_BUF_START;
+	printk("Using LCD memory address 0x%x\n", cfb->fb.screen_base);
+
+	cfb->currcon = -1;
+	strcpy(cfb->fb.fix.id, "psionw");
+	cfb->fb.fix.smem_len = LCD_BUF_SIZE;
+
+	cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+	cfb->fb.fix.type_aux = 0;
+	cfb->fb.fix.xpanstep = 0;
+	cfb->fb.fix.ypanstep = 0;
+	cfb->fb.fix.ywrapstep = 0;
+	cfb->fb.fix.accel = FB_ACCEL_NONE;
+
+	cfb->fb.var.nonstd = 0;
+	cfb->fb.var.activate = FB_ACTIVATE_NOW;
+	cfb->fb.var.height = -1;
+	cfb->fb.var.width = -1;
+	cfb->fb.var.accel_flags = 0;
+	cfb->fb.var.vmode = FB_VMODE_NONINTERLACED;
+
+	cfb->fb.var.xres = PSION_LCD_W;
+	cfb->fb.var.xres_virtual = PSION_LCD_W;
+	cfb->fb.var.yres = PSION_LCD_H;
+	cfb->fb.var.yres_virtual = PSION_LCD_H;
+	cfb->fb.var.bits_per_pixel = 4;
+	cfb->fb.var.pixclock = 288;
+	cfb->fb.var.grayscale = 1;
+
+	cfb->fb.fbops = &psionwfb_ops;
+	cfb->fb.changevar = NULL;
+	cfb->fb.switch_con = psionwfb_switch;
+	cfb->fb.updatevar = psionwfb_updatevar;
+	cfb->fb.blank = psionwfb_blank;
+	cfb->fb.flags = FBINFO_FLAG_DEFAULT;
+	cfb->fb.disp = (struct display *) (cfb + 1);
+	cfb->fb.pseudo_palette = (void *) (cfb->fb.disp + 1);
+
+	fb_alloc_cmap(&cfb->fb.cmap, CMAP_SIZE, 0);
+
+	/*
+	 * Power up the LCD
+	 */
+	psionw_lcd_disable();
+	psionw_lcd_init_hw();
+	psionw_lcd_enable();
+
+	ret = request_irq(IRQ_LCDINT, psionwfb_handle_irq, SA_INTERRUPT, "psionwfb", NULL);
+
+	if (ret) {
+		printk(KERN_ERR "psionwfb: request_irq failed: %d\n", ret);
+	}
+
+	psionwfb_set_var(&cfb->fb.var, -1, &cfb->fb);
+	err = register_framebuffer(&cfb->fb);
+
+      out:return err;
+}
+
+static void __exit
+psionwfb_exit(void)
+{
+	unregister_framebuffer(&cfb->fb);
+	kfree(cfb);
+
+	/*
+	 * Power down the LCD
+	 */
+}
+
+void
+psionw_lcd_disable()
+{
+	int lcdstate;
+
+	/* 
+	 * DC to DC converted may not be enabled, and clearing
+	 * PDDR_LCD_PWR without DC to DC converted causes the
+	 * screen not to come up on 5mx Pro.
+	 */
+	start_pump(1);
+
+	psionw_writeb(psionw_readb(PDDR) & ~PDDR_LCD_PWR, PDDR);
+
+	lcdstate = psionw_readl(LCDCTL);
+	lcdstate &= ~LCDCTL_EN;
+	psionw_writel(lcdstate, LCDCTL);
+}
+
+void
+psionw_lcd_enable()
+{
+	int lcdstate;
+
+	lcdstate = psionw_readl(LCDCTL);
+	lcdstate |= LCDCTL_BW;
+	lcdstate |= LCDCTL_EN;
+	psionw_writel(lcdstate, LCDCTL);
+
+	mdelay(30);
+
+	psionw_writeb(psionw_readb(PDDR) | PDDR_LCD_PWR, PDDR);
+}
+
+void
+psionwfb_set_timings(int xres, int yres, int pixclock)
+{
+	int lcdt0 = 0, lcdt1 = 0, lcdt2 = 0;
+
+	/* LCD horizontal timing */
+	lcdt0 |= PSION_HSW | PSION_HFP | PSION_HBP;
+	lcdt0 |= (xres - 16);
+	psionw_writel(lcdt0, LCDT0);
+
+	/* LCD vertical timing */
+	lcdt1 |= PSION_VSW | PSION_VFP | PSION_VBP;
+	lcdt1 |= (yres - 1);
+	psionw_writel(lcdt1, LCDT1);
+
+	/* Pixel clock and AC-bias frequency */
+	lcdt2 = psionw_readl(LCDT2);
+	lcdt2 &= ~0xff;
+	lcdt2 |= get_pcd(pixclock);
+	lcdt2 |= PSION_ACB;
+	psionw_writel(lcdt2, LCDT2);
+}
+
+void
+psionw_lcd_init_hw(void)
+{
+	int lcdstate;
+
+	/* Start DC to DC converter if not started */
+	start_pump(1);
+	
+	/* Set the default contrast */
+	psionw_writeb(psionw_readb(PBDR) | LCD_DEF_CMASK, PBDR);
+	
+	/* Write the current address to the LCD controller */
+	psionw_writel(LCD_MEM_START, LCD_DBAR1);
+}
+
+void
+psionw_lcd_powerdown(int lock)
+{
+	/* Turn off the LCD backlight. */
+	psionw_writeb(psionw_readb(PCDR) & ~PCDR_LIGHT, PCDR);
+
+	/* Turn off the LCD power */
+	psionw_writeb(psionw_readb(PDDR) & ~PDDR_LCD_PWR, PDDR);
+
+	/* Power down the LCD controller */
+	psionw_writel(psionw_readl(LCDCTL) & ~LCDCTL_EN, LCDCTL);
+}
+
+void
+psionw_lcd_powerup(int lock)
+{
+	psionw_lcd_disable();
+	psionw_lcd_init_hw();
+	psionw_lcd_enable();
+}
+
+#ifdef FBCON_HAS_MFB
+/*
+ * We need to override some fbcon_mbf functions to
+ * keep text colors black on white instead of white on black.
+ */
+struct display_switch fbcon_mfb_psionw = {
+	setup:fbcon_mfb_setup,
+	bmove:fbcon_mfb_bmove,
+	clear:fbcon_mfb_clear_psionw,
+	putc:fbcon_mfb_putc_psionw,
+	putcs:fbcon_mfb_putcs_psionw,
+	revc:fbcon_mfb_revc,
+	clear_margins:fbcon_mfb_clear_margins,
+	fontwidthmask:FONTWIDTH(8)
+};
+
+void
+fbcon_mfb_clear_psionw(struct vc_data *conp, struct display *p,
+		       int sy, int sx, int height, int width)
+{
+	u8 *dest;
+	u_int rows;
+	int inverse = conp ? attr_reverse(p, conp->vc_video_erase_char) : 0;
+
+	dest = p->screen_base + sy * fontheight(p) * p->next_line + sx;
+
+	if (sx == 0 && width == p->next_line) {
+		if (inverse)
+			fb_memclear(dest, height * fontheight(p) * width);
+		else
+			fb_memset255(dest, height * fontheight(p) * width);
+	} else
+		for (rows = height * fontheight(p); rows--;
+		     dest += p->next_line)
+			if (inverse)
+				fb_memclear_small(dest, width);
+			else
+				fb_memset255(dest, width);
+}
+
+void
+fbcon_mfb_putc_psionw(struct vc_data *conp, struct display *p,
+		      int c, int yy, int xx)
+{
+	u8 *dest, *cdat;
+	u_int rows, bold, revs, underl;
+	u8 d;
+
+	dest = p->screen_base + yy * fontheight(p) * p->next_line + xx;
+	cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+	bold = attr_bold(p, c);
+	revs = attr_reverse(p, c);
+	underl = attr_underline(p, c);
+
+	for (rows = fontheight(p); rows--; dest += p->next_line) {
+		d = *cdat++;
+		if (underl && !rows)
+			d = 0xff;
+		else if (bold)
+			d |= d >> 1;
+		if (!revs)
+			d = ~d;
+		fb_writeb(d, dest);
+	}
+}
+
+/* Modified to reverse the bits for Psion */
+void
+fbcon_mfb_putcs_psionw(struct vc_data *conp, struct display *p,
+		       const unsigned short *s, int count, int yy, int xx)
+{
+	u8 *dest, *dest0, *cdat;
+	u_int rows, bold, revs, underl;
+	u8 d;
+	u16 c;
+
+	dest0 = p->screen_base + yy * fontheight(p) * p->next_line + xx;
+	c = scr_readw(s);
+	bold = attr_bold(p, c);
+	revs = attr_reverse(p, c);
+	underl = attr_underline(p, c);
+
+	while (count--) {
+		c = scr_readw(s++) & p->charmask;
+		dest = dest0++;
+		cdat = p->fontdata + c * fontheight(p);
+		for (rows = fontheight(p); rows--; dest += p->next_line) {
+			d = *cdat++;
+
+			// Must reverse the bits for Psion
+			d = bi_reverse(d, 8);
+
+			if (underl && !rows)
+				d = 0xff;
+			else if (bold)
+				d |= d >> 1;
+
+			if (!revs)
+				d = ~d;
+			fb_writeb(d, dest);
+		}
+	}
+}
+#endif				/* FBCON_HAS_MFB */
+
+#ifdef FBCON_HAS_CFB2
+/*
+ * We need to override some fbcon_cfb4 functions to
+ * keep text colors black on white instead of white on black.
+ */
+struct display_switch fbcon_cfb2_psionw = {
+	setup:fbcon_cfb2_setup,
+	bmove:fbcon_cfb2_bmove,
+	clear:fbcon_cfb2_clear_psionw,
+	putc:fbcon_cfb2_putc_psionw,
+	putcs:fbcon_cfb2_putcs_psionw,
+	revc:fbcon_cfb2_revc,
+	fontwidthmask:FONTWIDTH(8)
+};
+
+void
+fbcon_cfb2_clear_psionw(struct vc_data *conp, struct display *p, int sy, int sx,
+			int height, int width)
+{
+	u8 *dest0, *dest;
+	int bytes = p->next_line, lines = height * fontheight(p), rows, i;
+	u32 bgx;
+
+	dest = p->screen_base + sy * fontheight(p) * bytes + sx * 2;
+
+	bgx = attr_bgcol_ec(p, conp);
+	bgx |= (bgx << 2);	/* expand the colour to 16 bits */
+	bgx |= (bgx << 4);
+	bgx |= (bgx << 8);
+
+	if (sx == 0 && width * 2 == bytes) {
+		for (i = 0; i < lines * width; i++) {
+			fb_writew(bgx ^ 0xffff, dest);
+			dest += 2;
+		}
+	} else {
+		dest0 = dest;
+		for (rows = lines; rows--; dest0 += bytes) {
+			dest = dest0;
+			for (i = 0; i < width; i++) {
+				/* memset ?? */
+				fb_writew(bgx ^ 0xffff, dest);
+				dest += 2;
+			}
+		}
+	}
+}
+
+void
+fbcon_cfb2_putc_psionw(struct vc_data *conp, struct display *p, int c, int yy,
+		       int xx)
+{
+	u8 *dest, *cdat;
+	int bytes = p->next_line, rows;
+	u32 eorx, fgx, bgx;
+
+	dest = p->screen_base + yy * fontheight(p) * bytes + xx * 2;
+	cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+
+	fgx = 3;		/*attr_fgcol(p,c); */
+	bgx = attr_bgcol(p, c);
+	fgx |= (fgx << 2);	/* expand color to 8 bits */
+	fgx |= (fgx << 4);
+	bgx |= (bgx << 2);
+	bgx |= (bgx << 4);
+	eorx = fgx ^ bgx;
+
+	for (rows = fontheight(p); rows--; dest += bytes) {
+		fb_writeb(((nibbletab_cfb2_psionw[*cdat >> 4] & eorx) ^ bgx) ^
+			  0xff, dest + 0);
+		fb_writeb(((nibbletab_cfb2_psionw[*cdat++ & 0xf] & eorx) ^ bgx)
+			  ^ 0xff, dest + 1);
+	}
+}
+
+/* Modify the inverse functionality for Psion */
+void
+fbcon_cfb2_putcs_psionw(struct vc_data *conp, struct display *p,
+			const unsigned short *s, int count, int yy, int xx)
+{
+	u8 *cdat, *dest, *dest0;
+	u16 c;
+	int rows, bytes = p->next_line;
+	u32 eorx, fgx, bgx;
+
+	dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * 2;
+	c = scr_readw(s);
+
+	/*
+	 * This part is commented out in the original fbcon_cfb2_putcs.
+	 * NOTE: Some inversion does not work, like typing a wrong 
+	 * character in top.
+	 */
+	fgx = attr_fgcol(p, c);
+
+	bgx = attr_bgcol(p, c);
+	fgx |= (fgx << 2);
+	fgx |= (fgx << 4);
+	bgx |= (bgx << 2);
+	bgx |= (bgx << 4);
+	eorx = fgx ^ bgx;
+	while (count--) {
+		c = scr_readw(s++) & p->charmask;
+		cdat = p->fontdata + c * fontheight(p);
+
+		for (rows = fontheight(p), dest = dest0; rows--; dest += bytes) {
+			fb_writeb(((nibbletab_cfb2_psionw[*cdat >> 4] & eorx) ^
+				   bgx) ^ 0xff, dest + 0);
+			fb_writeb(((nibbletab_cfb2_psionw[*cdat++ & 0xf] & eorx)
+				   ^ bgx) ^ 0xff, dest + 1);
+		}
+		dest0 += 2;
+	}
+}
+#endif				/* FBCON_HAS_CFB2 */
+
+#ifdef FBCON_HAS_CFB4
+/*
+ * We need to override some fbcon_cfb4 functions to
+ * keep text colors black on white instead of white on black.
+ */
+struct display_switch fbcon_cfb4_psionw = {
+	setup:fbcon_cfb4_setup,
+	bmove:fbcon_cfb4_bmove,
+	clear:fbcon_cfb4_clear_psionw,
+	putc:fbcon_cfb4_putc_psionw,
+	putcs:fbcon_cfb4_putcs_psionw,
+	revc:fbcon_cfb4_revc,
+	fontwidthmask:FONTWIDTH(8)
+};
+
+/*
+ * Slightly modified from the fbcon_cfb4_clear. We need to reverse 
+ * the background color to keep the text background white.
+ */
+void
+fbcon_cfb4_clear_psionw(struct vc_data *conp, struct display *p, int sy, int sx,
+			int height, int width)
+{
+	u8 *dest0, *dest;
+	int bytes = p->next_line, lines = height * fontheight(p), rows, i;
+	u32 bgx;
+
+	dest = p->screen_base + sy * fontheight(p) * bytes + sx * 4;
+
+	bgx = attr_bgcol_ec(p, conp);
+	bgx |= (bgx << 4);	/* expand the colour to 32bits */
+	bgx |= (bgx << 8);
+	bgx |= (bgx << 16);
+
+	if (sx == 0 && width * 4 == bytes) {
+		for (i = 0; i < lines * width; i++) {
+			fb_writel(bgx ^ 0xffffffff, dest);
+			dest += 4;
+		}
+	} else {
+		dest0 = dest;
+		for (rows = lines; rows--; dest0 += bytes) {
+			dest = dest0;
+			for (i = 0; i < width; i++) {
+				/* memset ?? */
+				fb_writel(bgx ^ 0xffffffff, dest);
+				dest += 4;
+			}
+		}
+	}
+}
+
+/*
+ * Slightly modified from the fbcon_cfb4_putc. We want to keep the 
+ * character color as black on white instead of white on black.
+ */
+void
+fbcon_cfb4_putc_psionw(struct vc_data *conp, struct display *p, int c, int yy,
+		       int xx)
+{
+	u8 *dest, *cdat;
+	int bytes = p->next_line, rows;
+	u32 eorx, fgx, bgx;
+
+	dest = p->screen_base + yy * fontheight(p) * bytes + xx * 4;
+	cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+
+	fgx = attr_fgcol(p, c);
+	bgx = attr_bgcol(p, c);
+	fgx |= (fgx << 4);
+	fgx |= (fgx << 8);
+	bgx |= (bgx << 4);
+	bgx |= (bgx << 8);
+	eorx = fgx ^ bgx;
+
+	for (rows = fontheight(p); rows--; dest += bytes) {
+		fb_writew(((nibbletab_cfb4_psionw[*cdat >> 4] & eorx) ^ bgx) ^
+			  0xff, dest + 0);
+		fb_writew(((nibbletab_cfb4_psionw[*cdat++ & 0xf] & eorx) ^ bgx)
+			  ^ 0xff, dest + 2);
+	}
+}
+
+/*
+ * Slightly modified from the fbcon_cfb4_putcs. We want to keep the 
+ * string color as black on white instead of white on black.
+ */
+void
+fbcon_cfb4_putcs_psionw(struct vc_data *conp, struct display *p,
+			const unsigned short *s, int count, int yy, int xx)
+{
+	u8 *cdat, *dest, *dest0;
+	u16 c;
+	int rows, bytes = p->next_line;
+	u32 eorx, fgx, bgx;
+
+	dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * 4;
+	c = scr_readw(s);
+	fgx = attr_fgcol(p, c);
+	bgx = attr_bgcol(p, c);
+	fgx |= (fgx << 4);
+	fgx |= (fgx << 8);
+	fgx |= (fgx << 16);
+	bgx |= (bgx << 4);
+	bgx |= (bgx << 8);
+	bgx |= (bgx << 16);
+
+	eorx = fgx ^ bgx;
+	while (count--) {
+		c = scr_readw(s++) & p->charmask;
+		cdat = p->fontdata + c * fontheight(p);
+
+		for (rows = fontheight(p), dest = dest0; rows--; dest += bytes) {
+			fb_writew(((nibbletab_cfb4_psionw[*cdat >> 4] & eorx) ^
+				   bgx) ^ 0xffffffff, dest + 0);
+			fb_writew(((nibbletab_cfb4_psionw[*cdat++ & 0xf] & eorx)
+				   ^ bgx) ^ 0xffffffff, dest + 2);
+		}
+		dest0 += 4;
+	}
+}
+#endif				/* FBCON_HAS_CFB4 */
+
+#ifdef MODULE
+module_init(psionwfb_init);
+#endif
+module_exit(psionwfb_exit);
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/psionwfb.h linux-2.4.19-rmk2/drivers/video/psionwfb.h
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/psionwfb.h linux-2.4.19-rmk2/drivers/video/psionwfb.h
--- /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/video/psionwfb.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.19-rmk2/drivers/video/psionwfb.h	2002-10-02 22:07:30.000000000 -0700
@@ -0,0 +1,145 @@
+/*
+ * linux/drivers/video/psionwfb.h
+ */
+
+#define CMAP_SIZE	16
+
+static struct psionwfb_info {
+	struct fb_info fb;
+	int currcon;
+} *cfb;
+
+void psionw_lcd_disable(void);
+void psionw_lcd_enable(void);
+void psionw_lcd_init_hw(void);
+
+#ifdef DEBUG
+extern void serial_printf(char *fmt, ...);
+#endif
+
+struct display_switch fbcon_mfb_psionw;
+struct display_switch fbcon_cfb2_psionw;
+struct display_switch fbcon_cfb4_psionw;
+
+/* 1bpp functions */
+extern void fbcon_mfb_setup(struct display *p);
+extern void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			    int height, int width);
+void fbcon_mfb_clear_psionw(struct vc_data *conp, struct display *p,
+			    int sy, int sx, int height, int width);
+void fbcon_mfb_putc_psionw(struct vc_data *conp, struct display *p,
+			   int c, int yy, int xx);
+void fbcon_mfb_putcs_psionw(struct vc_data *conp, struct display *p,
+			    const unsigned short *s, int count, int yy, int xx);
+extern void fbcon_mfb_revc(struct display *p, int xx, int yy);
+extern void fbcon_mfb_clear_margins(struct vc_data *conp, struct display *p,
+				    int bottom_only);
+
+/* 2bpp functions */
+extern void fbcon_cfb2_setup(struct display *p);
+extern void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width);
+void fbcon_cfb2_clear_psionw(struct vc_data *conp, struct display *p,
+			     int sy, int sx, int height, int width);
+void fbcon_cfb2_putc_psionw(struct vc_data *conp, struct display *p,
+			    int c, int yy, int xx);
+void fbcon_cfb2_putcs_psionw(struct vc_data *conp, struct display *p,
+			     const unsigned short *s, int count, int yy,
+			     int xx);
+extern void fbcon_cfb2_revc(struct display *p, int xx, int yy);
+
+/* 4bpp functions */
+extern void fbcon_cfb4_setup(struct display *p);
+extern void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width);
+void fbcon_cfb4_clear_psionw(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width);
+void fbcon_cfb4_putc_psionw(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx);
+void fbcon_cfb4_putcs_psionw(struct vc_data *conp, struct display *p,
+			     const unsigned short *s, int count, int yy,
+			     int xx);
+extern void fbcon_cfb4_revc(struct display *p, int xx, int yy);
+
+static u_char nibbletab_cfb2_psionw[] = {
+#if defined(__BIG_ENDIAN)
+	0x00, 0x03, 0x0c, 0x0f,
+	0x30, 0x33, 0x3c, 0x3f,
+	0xc0, 0xc3, 0xcc, 0xcf,
+	0xf0, 0xf3, 0xfc, 0xff
+#elif defined(__LITTLE_ENDIAN)
+	0x00, 0xc0, 0x30, 0xf0,
+	0x0c, 0xcc, 0x3c, 0xfc,
+	0x03, 0xc3, 0x33, 0xf3,
+	0x0f, 0xcf, 0x3f, 0xff
+#else
+#error FIXME: No endianness??
+#endif
+};
+
+static u16 nibbletab_cfb4_psionw[] = {
+
+#if defined(__BIG_ENDIAN)
+	0x0000, 0x000f, 0x00f0, 0x00ff,
+	0x0f00, 0x0f0f, 0x0ff0, 0x0fff,
+	0xf000, 0xf00f, 0xf0f0, 0xf0ff,
+	0xff00, 0xff0f, 0xfff0, 0xffff
+#elif defined(__LITTLE_ENDIAN)
+	0x0000, 0xf000, 0x0f00, 0xff00,
+	0x00f0, 0xf0f0, 0x0ff0, 0xfff0,
+	0x000f, 0xf00f, 0x0f0f, 0xff0f,
+	0x00ff, 0xf0ff, 0x0fff, 0xffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+
+static inline unsigned
+bi_reverse(unsigned code, int len)
+{
+	register unsigned res = 0;
+	do {
+		res |= code & 1;
+		code >>= 1, res <<= 1;
+	} while (--len > 0);
+	return res >> 1;
+}
+
+#ifdef DEBUG
+/*
+ * Dumps the color palette to serial terminal
+ */
+void
+dump_pal()
+{
+	int i, val, phys;
+	serial_printf("*** Dumping color palette ***\n");
+	for (i = 0; i < LCDPAL; i += 4) {
+		val = *(volatile unsigned int *) (LCD_PAL_START + i);
+		if (i == 0) {
+			switch ((val >> 12) & 0x7) {
+			case 0:
+				serial_printf("Mode: 1bpp\n");
+				break;
+			case 1:
+				serial_printf("Mode: 2bpp\n");
+				break;
+			case 2:
+				serial_printf("Mode: 4bpp\n");
+				break;
+			case 3:
+				serial_printf("Mode: 8bpp\n");
+				break;
+			default:
+				serial_printf("Mode: Unknown\n");
+			}
+		}
+		serial_printf("0x%04x  ", val & 0xffff);
+		serial_printf("0x%04x  ", (val >> 16));
+		if ((i & 0x7) == 0x4) {
+			serial_printf("\n");
+		}
+	}
+	serial_printf("\n");
+}
+#endif
