--- linux-2.6.0-test8-rmk1/include/linux/lcd-module.h	1969-12-31 19:00:00.000000000 -0500
+++ kernel26/include/linux/lcd-module.h	2003-11-02 21:39:09.000000000 -0500
@@ -0,0 +1,53 @@
+/*
+ * LCD/Backlight Lowlevel Control Abstraction
+ * 
+ * Copyright (C) 2003 Hewlett-Packard Company
+ * 
+ */
+
+#ifndef _LINUX_LCD_H
+#define _LINUX_LCD_H
+
+#ifndef _LINUX_DEVICE_H_
+#include <linux/device.h>
+#endif /* _LINUX_DEVICE_H_ */
+
+struct backlight_module {
+	const char *name;
+	struct module *owner;
+	int (*set_power)(struct backlight_module *, int power);
+	int (*get_power)(struct backlight_module *, int *power);
+	int (*set_brightness)(struct backlight_module *, int brightness);
+	int (*get_brightness)(struct backlight_module *, int *brightness);
+	struct class_device class_dev;
+};
+
+extern int backlight_module_register(struct backlight_module *);
+extern void backlight_module_unregister(struct backlight_module *);
+
+extern struct backlight_module *backlight_module_get(const char *backlight_module_name);
+extern void backlight_module_put(struct backlight_module *backlight_module);
+
+#define to_backlight_module(obj) container_of(obj, struct backlight_module, class_dev)
+
+struct lcd_module {
+	const char *name;
+	struct module *owner;
+	int (*get_mach_info)(struct lcd_module *, void **mach_info);
+	int (*get_power)(struct lcd_module *, int *power);
+	int (*set_power)(struct lcd_module *, int power);
+	int (*get_enable)(struct lcd_module *, int *enable);
+	int (*set_enable)(struct lcd_module *, int enable);
+	int (*get_contrast)(struct lcd_module *, int *contrast);
+	int (*set_contrast)(struct lcd_module *, int contrast);
+	struct class_device class_dev;
+};
+
+extern int lcd_module_register(struct lcd_module *);
+extern void lcd_module_unregister(struct lcd_module *);
+
+extern struct lcd_module *lcd_module_get(const char *lcd_module_name);
+extern void lcd_module_put(struct lcd_module *lcd_module);
+
+#define to_lcd_module(obj) container_of(obj, struct lcd_module, class_dev)
+#endif
--- linux-2.6.0-test8-rmk1/drivers/video/lcd-module.c	1969-12-31 19:00:00.000000000 -0500
+++ kernel26/drivers/video/lcd-module.c	2003-11-04 12:44:22.000000000 -0500
@@ -0,0 +1,381 @@
+/*
+ * LCD/Backlight Lowlevel Control Abstraction
+ * 
+ * Copyright (C) 2003 Hewlett-Packard Company
+ * 
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/lcd-module.h>
+#include <asm/bug.h>
+
+static int has_initted = 0;
+
+static int backlight_class_hotplug(struct class_device *dev, char **envp, 
+				   int num_envp, char *buffer, int buffer_size);  
+void backlight_class_release(struct class_device *dev);
+
+struct class backlight_class = {
+	.name = "backlight",
+	.hotplug = backlight_class_hotplug,
+	.release = backlight_class_release,
+};
+
+static ssize_t backlight_show_name(struct class_device *cdev, char *buf)
+{
+	struct backlight_module *lm = to_backlight_module(cdev);
+	if (lm) {
+		strcpy(buf, lm->name);
+		return strlen(buf);
+	} else {
+		return 0;
+	}
+}
+
+static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
+{
+	struct backlight_module *bl = to_backlight_module(cdev);
+	if (bl && bl->get_power) {
+		int power;
+		bl->get_power(bl, &power);
+		sprintf(buf, "%d\n", power);
+		return strlen(buf);
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t backlight_store_power(struct class_device *cdev, const char * buf, size_t count)
+{
+	struct backlight_module *bl = to_backlight_module(cdev);
+	if (bl && bl->set_power) {
+		int power;
+		sscanf(buf, "%d", &power);
+		bl->set_power(bl, power);
+		return count;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
+{
+	struct backlight_module *bl = to_backlight_module(cdev);
+	if (bl && bl->get_brightness) {
+		int brightness;
+		bl->get_brightness(bl, &brightness);
+		sprintf(buf, "%d\n", brightness);
+		return strlen(buf);
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t backlight_store_brightness(struct class_device *cdev, const char * buf, size_t count)
+{
+	struct backlight_module *bl = to_backlight_module(cdev);
+	if (bl && bl->set_brightness) {
+		int brightness;
+		sscanf(buf, "%d", &brightness);
+		bl->set_brightness(bl, brightness);
+		return count;
+	} else {
+		return -ENODEV;
+	}
+}
+
+#define BL_CLASS_DEVICE_ATTR(_name,_mode,_show,_store)		\
+struct class_device_attribute bl_class_device_attr_##_name = { 	\
+	.attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE },	\
+	.show	= _show,					\
+	.store	= _store,					\
+};
+
+static BL_CLASS_DEVICE_ATTR (name, 0444, backlight_show_name, NULL);
+static BL_CLASS_DEVICE_ATTR (power, 0644, backlight_show_power, backlight_store_power);
+static BL_CLASS_DEVICE_ATTR (brightness, 0644, backlight_show_brightness, backlight_store_brightness);
+
+static struct class_device_attribute *bl_class_device_attributes[] = {
+	&bl_class_device_attr_name,
+	&bl_class_device_attr_power,
+	&bl_class_device_attr_brightness
+};
+
+extern int backlight_module_register(struct backlight_module *bl)
+{
+	int rc = 0;
+	int i;
+	if (1) {
+		WARN_ON(!bl->name);
+		bl->class_dev.class = &backlight_class;
+		strcpy(bl->class_dev.class_id, bl->name);
+		rc =  class_device_register(&bl->class_dev);
+		if (rc) {
+			class_device_unregister(&bl->class_dev);
+			return rc;
+		}
+		for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
+			rc = class_device_create_file(&bl->class_dev,
+						      bl_class_device_attributes[i]);
+			if (rc)
+				return rc;
+		}
+	}
+	return rc;
+}
+
+extern void backlight_module_unregister(struct backlight_module *bl)
+{
+	class_device_unregister(&bl->class_dev);
+}
+
+
+EXPORT_SYMBOL(backlight_module_register);
+EXPORT_SYMBOL(backlight_module_unregister);
+EXPORT_SYMBOL(backlight_module_get);
+EXPORT_SYMBOL(backlight_module_put);
+
+
+extern struct backlight_module *backlight_module_get(const char *bl_name)
+{
+	struct list_head * entry;
+	list_for_each(entry, &backlight_class.children) {
+		struct class_device *class_dev = container_of(entry, struct class_device, node);
+		class_dev = class_device_get(class_dev);
+		if (class_dev) {
+			struct backlight_module *bl = to_backlight_module(class_dev);
+			if ((strcmp(class_dev->class_id, bl_name) == 0)
+			    && try_module_get(bl->owner)) {
+				return bl;
+			}
+			class_device_put(class_dev);
+			return NULL;
+		}
+	}
+	return NULL;
+}
+extern void backlight_module_put(struct backlight_module *backlight_module)
+{
+	if (backlight_module) {
+		module_put(backlight_module->owner);
+		class_device_put(&backlight_module->class_dev);
+	}
+}
+
+static int backlight_class_hotplug(struct class_device *dev, char **envp, 
+			     int num_envp, char *buffer, int buffer_size)
+{
+	return 0;
+}
+
+void backlight_class_release(struct class_device *dev)
+{
+	struct backlight_module *backlight_module = to_backlight_module(dev);
+}
+
+
+/************************************************************/  
+static int lcd_class_hotplug(struct class_device *dev, char **envp, 
+			     int num_envp, char *buffer, int buffer_size);  
+void lcd_class_release(struct class_device *dev);
+
+struct class lcd_class = {
+	.name = "lcd",
+	.hotplug = lcd_class_hotplug,
+	.release = lcd_class_release,
+};
+
+static ssize_t lcd_show_name(struct class_device *cdev, char *buf)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm) {
+		strcpy(buf, lm->name);
+		return strlen(buf);
+	} else {
+		return 0;
+	}
+}
+
+static ssize_t lcd_show_power(struct class_device *cdev, char *buf)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm && lm->get_power) {
+		int power;
+		lm->get_power(lm, &power);
+		sprintf(buf, "%d\n", power);
+		return strlen(buf);
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t lcd_store_power(struct class_device *cdev, const char * buf, size_t count)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm && lm->set_power) {
+		int power;
+		sscanf(buf, "%d", &power);
+		lm->set_power(lm, power);
+		return count;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t lcd_show_enable(struct class_device *cdev, char *buf)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm && lm->get_enable) {
+		int enable;
+		lm->get_enable(lm, &enable);
+		sprintf(buf, "%d\n", enable);
+		return strlen(buf);
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t lcd_store_enable(struct class_device *cdev, const char * buf, size_t count)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm && lm->set_enable) {
+		int enable;
+		sscanf(buf, "%d", &enable);
+		lm->set_enable(lm, enable);
+		return count;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm && lm->get_contrast) {
+		int contrast;
+		lm->get_contrast(lm, &contrast);
+		sprintf(buf, "%d\n", contrast);
+		return strlen(buf);
+	} else {
+		return -ENODEV;
+	}
+}
+
+static ssize_t lcd_store_contrast(struct class_device *cdev, const char * buf, size_t count)
+{
+	struct lcd_module *lm = to_lcd_module(cdev);
+	if (lm && lm->set_contrast) {
+		int contrast;
+		sscanf(buf, "%d", &contrast);
+		lm->set_contrast(lm, contrast);
+		return count;
+	} else {
+		return -ENODEV;
+	}
+}
+
+
+static CLASS_DEVICE_ATTR (name, 0444, lcd_show_name, NULL);
+static CLASS_DEVICE_ATTR (power, 0644, lcd_show_power, lcd_store_power);
+static CLASS_DEVICE_ATTR (enable, 0644, lcd_show_enable, lcd_store_enable);
+static CLASS_DEVICE_ATTR (contrast, 0644, lcd_show_contrast, lcd_store_contrast);
+
+static struct class_device_attribute *lcd_class_device_attributes[] = {
+	&class_device_attr_name,
+	&class_device_attr_power,
+	&class_device_attr_enable,
+	&class_device_attr_contrast
+};
+
+extern int lcd_module_register(struct lcd_module *lm)
+{
+	int rc = 0;
+	int i;
+	if (1) {
+		WARN_ON(!lm->name);
+		printk("lcd_module_register: name=%s\n", lm->name);
+		lm->class_dev.class = &lcd_class;
+		strcpy(lm->class_dev.class_id, lm->name);
+		lm->class_dev.class = &lcd_class;
+		rc = class_device_register(&lm->class_dev);
+		if (rc)
+			return rc;
+		for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) {
+			rc = class_device_create_file(&lm->class_dev,
+						      lcd_class_device_attributes[i]);
+			if (rc)
+				return rc;
+		}
+	}
+	return 0;
+}
+
+extern void lcd_module_unregister(struct lcd_module *lm)
+{
+	class_device_unregister(&lm->class_dev);
+}
+
+extern struct lcd_module *lcd_module_get(const char *lm_name)
+{
+	struct list_head * entry;
+	
+	if (!has_initted)
+	{
+		printk("Oh dear. We haven't been initted yet. (%s:%s:%d)\n", __FILE__, __FUNCTION__, __LINE__);
+		return NULL;
+	}
+	list_for_each(entry, &lcd_class.children) {
+		struct class_device *class_dev = container_of(entry, struct class_device, node);
+		class_dev = class_device_get(class_dev);
+		if (class_dev) {
+			struct lcd_module *lm = to_lcd_module(class_dev);
+			if ((strcmp(class_dev->class_id, lm_name) == 0)
+			    && try_module_get(lm->owner)) {
+				return lm;
+			}
+			class_device_put(class_dev);
+			return NULL;
+		}
+	}
+	return NULL;
+}
+extern void lcd_module_put(struct lcd_module *lcd_module)
+{
+	if (lcd_module) {
+		module_put(lcd_module->owner);
+		class_device_put(&lcd_module->class_dev);
+	}
+}
+
+static int lcd_class_hotplug(struct class_device *dev, char **envp, 
+			     int num_envp, char *buffer, int buffer_size)
+{
+	return 0;
+}
+
+void lcd_class_release(struct class_device *dev)
+{
+	struct lcd_module *lcd_module = to_lcd_module(dev);
+}
+
+static int __init lcd_class_init(void)
+{
+	int rc;
+	rc = class_register(&backlight_class);
+	if (rc) return rc;
+	rc = class_register(&lcd_class);
+	if (rc) return rc;
+
+	has_initted++;
+
+	return 0;
+}
+postcore_initcall(lcd_class_init);
+
+EXPORT_SYMBOL(lcd_module_register);
+EXPORT_SYMBOL(lcd_module_unregister);
+EXPORT_SYMBOL(lcd_module_get);
+EXPORT_SYMBOL(lcd_module_put);
--- linux-2.6.0-test8-rmk1/drivers/video/sa1100fb.c	2003-10-21 14:20:57.000000000 -0400
+++ kernel26/drivers/video/sa1100fb.c	2003-11-04 13:27:39.000000000 -0500
@@ -175,6 +175,7 @@
 #include <linux/cpufreq.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/lcd-module.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
@@ -197,8 +198,10 @@
 
 #include "sa1100fb.h"
 
+#ifndef CONFIG_LCD_MODULE
 extern void (*sa1100fb_backlight_power)(int on);
 extern void (*sa1100fb_lcd_power)(int on);
+#endif
 
 /*
  * IMHO this looks wrong.  In 8BPP, length should be 8.
@@ -649,6 +652,18 @@
 	 * h3600    {12,4}, { 7,4}, { 1,4}, { 0,0}
 	 * freebird { 8,4}, { 4,4}, { 0,4}, {12,4}
 	 */
+#ifdef CONFIG_LCD_MODULE
+	struct lcd_module *lm = lcd_module_get("sa1100fb");
+	if (lm) {
+		if (lm
+		    && lm->get_mach_info 
+		    && !lm->get_mach_info(lm, (void **)&inf)) {
+			lcd_module_put(lm);
+			return inf;
+		}
+	}
+#endif	
+
 #ifdef CONFIG_SA1100_ASSABET
 	if (machine_is_assabet()) {
 #ifndef ASSABET_PAL_VIDEO
@@ -1252,16 +1267,36 @@
 {
 	DPRINTK("backlight o%s\n", on ? "n" : "ff");
 
+#ifdef CONFIG_LCD_MODULE
+	{
+		struct backlight_module *bl = backlight_module_get("sa1100fb");
+		if (bl && bl->set_power) {
+			bl->set_power(bl, on);
+			backlight_module_put(bl);
+		}
+	}
+#else
 	if (sa1100fb_backlight_power)
 		sa1100fb_backlight_power(on);
+#endif
 }
 
 static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on)
 {
 	DPRINTK("LCD power o%s\n", on ? "n" : "ff");
 
+#ifdef CONFIG_LCD_MODULE
+	{
+		struct lcd_module *lm = lcd_module_get("sa1100fb");
+		if (lm && lm->set_power) {
+			lm->set_power(lm, on);
+			lcd_module_put(lm);
+		}
+	}
+#else
 	if (sa1100fb_lcd_power)
 		sa1100fb_lcd_power(on);
+#endif
 }
 
 static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
--- linux-2.6.0-test8-rmk1/drivers/video/pxafb.c	1969-12-31 19:00:00.000000000 -0500
+++ kernel26/drivers/video/pxafb.c	2003-11-04 13:14:01.000000000 -0500
@@ -0,0 +1,1338 @@
+/*
+ *  linux/drivers/video/pxafb.c
+ *
+ *  Copyright (C) 1999 Eric A. Thomas
+ *   Based on acornfb.c Copyright (C) Russell King.
+ *
+ * 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.
+ *
+ *	        Intel PXA250/210 LCD Controller Frame Buffer Driver
+ *
+ * Please direct your questions and comments on this driver to the following
+ * email address:
+ *
+ *	linux-arm-kernel@lists.arm.linux.org.uk
+ *
+ *
+ * Code Status:
+ *
+ * 2003/08/29: Joshua Wise <joshua@joshuawise.com>
+ *	- Upported to kernel 2.6.
+ * 2001/08/03: <cbrake@accelent.com>
+ *      - Ported from SA1100 to PXA250
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/lcd-module.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/ipaq.h>
+
+/*
+ * debugging?
+ */
+#define DEBUG 0
+/*
+ * Complain if VAR is out of range.
+ */
+#define DEBUG_VAR 0
+
+#undef ASSABET_PAL_VIDEO
+
+#include "pxafb.h"
+
+
+void (*pxafb_lcd_power)(int on);
+EXPORT_SYMBOL(pxafb_lcd_power);
+
+void (*pxafb_backlight_power)(int on);
+EXPORT_SYMBOL(pxafb_backlight_power);
+
+void (*pxafb_blank_helper)(int blank);
+EXPORT_SYMBOL(pxafb_blank_helper);
+
+extern void (*pxafb_backlight_power)(int on);
+extern void (*pxafb_lcd_power)(int on);
+
+extern unsigned int get_clk_frequency_khz(int info);
+
+/*
+ * IMHO this looks wrong.  In 8BPP, length should be 8.
+ */
+static struct pxafb_rgb rgb_8 = {
+	.red	= { .offset = 0,  .length = 4, },
+	.green	= { .offset = 0,  .length = 4, },
+	.blue	= { .offset = 0,  .length = 4, },
+	.transp	= { .offset = 0,  .length = 0, },
+};
+
+static struct pxafb_rgb def_rgb_16 = {
+	.red	= { .offset = 11, .length = 5, },
+	.green	= { .offset = 5,  .length = 6, },
+	.blue	= { .offset = 0,  .length = 5, },
+	.transp	= { .offset = 0,  .length = 0, },
+};
+
+#ifdef LCD_PIXCLOCK
+static struct pxafb_mach_info pxa_fb_info __initdata = {
+	.pixclock = 	LCD_PIXCLOCK,	/* clock period in ps */
+	.bpp = 		LCD_BPP,
+	.xres = 	LCD_XRES,
+	.yres = 	LCD_YRES,
+	.hsync_len = 	LCD_HORIZONTAL_SYNC_PULSE_WIDTH,
+	.vsync_len = 	LCD_VERTICAL_SYNC_PULSE_WIDTH,
+	.left_margin = 	LCD_BEGIN_OF_LINE_WAIT_COUNT,
+	.upper_margin =	LCD_BEGIN_FRAME_WAIT_COUNT,
+	.right_margin =	LCD_END_OF_LINE_WAIT_COUNT,
+	.lower_margin =	LCD_END_OF_FRAME_WAIT_COUNT,
+	.sync =		LCD_SYNC,
+	.lccr0 =	LCD_LCCR0,
+	.lccr3 =	LCD_LCCR3
+};
+#endif
+
+#ifdef CONFIG_ARCH_H3900
+/* read LCCR0: 0x00000081 */
+/* read LCCR1: 0x0b10093f -- BLW=0x0b, ELW=0x10, HSW=2, PPL=0x13f */
+/* read LCCR2: 0x051008ef -- BFW=0x05, EFW=0x10, VSW=2, LPP=0xef */
+/* read LCCR3: 0x0430000a */
+static struct pxafb_mach_info h3900_fb_info __initdata = {
+	.pixclock =	221039,	/* clock period in ps */
+	.bpp =		16,
+	.xres =		320,
+	.yres =		240,
+	.hsync_len =	3,
+	.vsync_len =	3,
+	.left_margin =	12,
+	.upper_margin =	6,
+	.right_margin =	17,
+	.lower_margin =	17,
+	.sync =		0, /* both horiz and vert active low sync */
+	.lccr0 =	(LCCR0_PAS | LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM  | LCCR0_OUM),
+	.lccr3 =	(LCCR3_16BPP)
+};
+#endif
+
+#ifdef CONFIG_ARCH_H1900
+/* h1900: LCCR0: 0x3008f9	-- ENB | LDM | SFM | IUM | EFM | PAS | QDM | BM | OUM
+	  LCCR1: 0x1a0a10ef	-- PPL=0xef, HSW=0x4, ELW=0xA, BLW=0x1A
+          LCCR2: 0x303013f	-- BFW=0x0, EFW=0x30, VSW=0xC, LPP=0x13F
+          LCCR3: 0x4700008      -- PCD=0x8, ACB=0x0, API=0x0, VSP, HSP, PCP, BPP=0x4 */
+          
+static struct pxafb_mach_info h1900_fb_info __initdata = {
+	.pixclock =	0,
+	.bpp =		16,
+	.xres =		240,	/* H1900's display is portrait */
+	.yres =		320,
+	.hsync_len =	5,
+	.vsync_len =	13,
+	.left_margin =	27,
+	.upper_margin =	1,
+	.right_margin =	11,
+	.lower_margin =	49,
+	.sync =		0,
+	.lccr0 =	(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS | LCCR0_QDM | LCCR0_BM | LCCR0_OUM),
+	.lccr3 =	(LCCR3_HorSnchL | LCCR3_VrtSnchL | LCCR3_16BPP | LCCR3_PCP | /*PCD */ 0x8)
+};
+#endif
+
+static struct pxafb_mach_info * __init
+pxafb_get_machine_info(struct pxafb_info *fbi)
+{
+#ifdef CONFIG_LCD_MODULE
+	struct pxafb_mach_info *inf;
+	struct lcd_module *lm = lcd_module_get("pxafb");
+	if (lm
+	    && lm->get_mach_info 
+	    && (lm->get_mach_info(lm, (void **)&inf) == 0)) {
+		return inf;
+	}
+#endif
+#ifdef CONFIG_ARCH_H1900
+	if (machine_is_h1900()) {
+		fbi->rgb[RGB_16] = &def_rgb_16;
+		return &h1900_fb_info;
+	};
+#endif
+#ifdef CONFIG_ARCH_H3900
+        if (machine_is_h3900()) {
+                fbi->rgb[RGB_16] = &def_rgb_16;
+                return &h3900_fb_info;
+        }
+#endif
+#ifdef LCD_PIXCLOCK
+	return &pxa_fb_info;
+#else
+        return NULL;
+#endif
+}
+
+static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *);
+static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);
+
+static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	/*
+	 * We need to handle two requests being made at the same time.
+	 * There are two important cases:
+	 *  1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
+	 *     We must perform the unblanking, which will do our REENABLE for us.
+	 *  2. When we are blanking, but immediately unblank before we have
+	 *     blanked.  We do the "REENABLE" thing here as well, just to be sure.
+	 */
+	if (fbi->task_state == C_ENABLE && state == C_REENABLE)
+		state = (u_int) -1;
+	if (fbi->task_state == C_DISABLE && state == C_ENABLE)
+		state = C_REENABLE;
+
+	if (state != (u_int)-1) {
+		fbi->task_state = state;
+		schedule_work(&fbi->task);
+	}
+	local_irq_restore(flags);
+}
+
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int
+pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
+		       u_int trans, struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+	unsigned int val, ret = 1;
+
+	if (regno < fbi->palette_size) {
+		val = ((red >> 0) & 0xf800);
+		val |= ((green >> 5) & 0x07e0);
+		val |= ((blue >> 11) & 0x001f);
+
+		fbi->palette_cpu[regno] = val;
+		ret = 0;
+	}
+	return ret;
+}
+
+static int
+pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+		   u_int trans, struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+	u_int val;
+	int ret = 1;
+
+	/*
+	 * If greyscale is true, then we convert the RGB value
+	 * to greyscale no mater what visual we are using.
+	 */
+	if (fbi->fb.var.grayscale)
+		red = green = blue = (19595 * red + 38470 * green +
+					7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+	case FB_VISUAL_DIRECTCOLOR:
+		/*
+		 * 12 or 16-bit True Colour.  We encode the RGB value
+		 * according to the RGB bitfield information.
+		 */
+		if (regno <= 16) {
+			u32 *pal = fbi->fb.pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+
+	case FB_VISUAL_PSEUDOCOLOR:
+		ret = pxafb_setpalettereg(regno, red, green, blue, trans, info);
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ *  pxafb_check_var():
+ *    Round up in the following order: bits_per_pixel, xres,
+ *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
+ *    bitfields, horizontal timing, vertical timing.
+ */
+static int
+pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+	int rgbidx = 0; // combat bogus warning
+
+	if (var->xres < MIN_XRES)
+		var->xres = MIN_XRES;
+	if (var->yres < MIN_YRES)
+		var->yres = MIN_YRES;
+	if (var->xres > fbi->max_xres)
+		var->xres = fbi->max_xres;
+	if (var->yres > fbi->max_yres)
+		var->yres = fbi->max_yres;
+	var->xres_virtual = max(var->xres_virtual, var->xres);
+	var->yres_virtual = max(var->yres_virtual, var->yres);
+
+	DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
+	switch (var->bits_per_pixel) {
+	case 4:  rgbidx = RGB_8; break;
+	case 8:  rgbidx = RGB_8; break;
+	case 12:
+		/* make sure we are in passive mode */
+		if (!(fbi->lccr0 & LCCR0_PAS))
+			rgbidx = RGB_16;
+		break;
+
+	case 16:
+		/* 
+		 * 16 bits works apparemtly fine in passive mode for those,
+		 * so don't complain
+		 */
+		if (machine_is_lubbock() ||
+		    machine_is_pxa_cerf()) {
+			rgbidx = RGB_16;
+		} else
+			/* make sure we are in active mode */
+			if ((fbi->lccr0 & LCCR0_PAS))
+				rgbidx = RGB_16;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * Copy the RGB parameters for this display
+	 * from the machine specific parameters.
+	 */
+	var->red    = fbi->rgb[rgbidx]->red;
+	var->green  = fbi->rgb[rgbidx]->green;
+	var->blue   = fbi->rgb[rgbidx]->blue;
+	var->transp = fbi->rgb[rgbidx]->transp;
+
+	DPRINTK("RGBT length = %d:%d:%d:%d\n",
+		var->red.length, var->green.length, var->blue.length,
+		var->transp.length);
+
+	DPRINTK("RGBT offset = %d:%d:%d\n",
+		var->red.offset, var->green.offset, var->blue.offset);
+
+	return 0;
+}
+
+static inline void pxafb_set_truecolor(u_int is_true_color)
+{
+	// noop
+}
+
+/*
+ * pxafb_set_par():
+ *	Set the user defined part of the display for the specified console
+ */
+static int
+pxafb_set_par(struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+	struct fb_var_screeninfo *var = &info->var;
+
+	DPRINTK("set_par\n");
+
+	if (var->bits_per_pixel == 16)
+		fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+	else if (!fbi->cmap_static)
+		fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	else {
+		/*
+		 * Some people have weird ideas about wanting static
+		 * pseudocolor maps.  I suspect their user space
+		 * applications are broken.
+		 */
+		fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+	}
+
+	fbi->fb.fix.line_length = var->xres_virtual *
+				  var->bits_per_pixel / 8;
+				  
+	/* 
+	 * Set (any) board control register to handle new color depth
+	 */
+	pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
+
+	pxafb_activate_var(var, fbi);
+
+	return 0;
+}
+
+#if 0
+static int
+pxafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+		  struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+
+	/*
+	 * Make sure the user isn't doing something stupid.
+	 */
+	if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->cmap_static))
+		return -EINVAL;
+ 
+	return gen_set_cmap(cmap, kspc, con, info);
+}
+#endif
+
+/*
+ * Formal definition of the VESA spec:
+ *  On
+ *  	This refers to the state of the display when it is in full operation
+ *  Stand-By
+ *  	This defines an optional operating state of minimal power reduction with
+ *  	the shortest recovery time
+ *  Suspend
+ *  	This refers to a level of power management in which substantial power
+ *  	reduction is achieved by the display.  The display can have a longer 
+ *  	recovery time from this state than from the Stand-by state
+ *  Off
+ *  	This indicates that the display is consuming the lowest level of power
+ *  	and is non-operational. Recovery from this state may optionally require
+ *  	the user to manually power on the monitor
+ *
+ *  Now, the fbdev driver adds an additional state, (blank), where they
+ *  turn off the video (maybe by colormap tricks), but don't mess with the
+ *  video itself: think of it semantically between on and Stand-By.
+ *
+ *  So here's what we should do in our fbdev blank routine:
+ *
+ *  	VESA_NO_BLANKING (mode 0)	Video on,  front/back light on
+ *  	VESA_VSYNC_SUSPEND (mode 1)  	Video on,  front/back light off
+ *  	VESA_HSYNC_SUSPEND (mode 2)  	Video on,  front/back light off
+ *  	VESA_POWERDOWN (mode 3)		Video off, front/back light off
+ *
+ *  This will match the matrox implementation.
+ */
+/*
+ * pxafb_blank():
+ *	Blank the display by setting all palette values to zero.  Note, the 
+ * 	12 and 16 bpp modes don't really use the palette, so this will not
+ *      blank the display in all modes.  
+ */
+static int pxafb_blank(int blank, struct fb_info *info)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *)info;
+	int i;
+
+	DPRINTK("pxafb_blank: blank=%d\n", blank);
+
+	switch (blank) {
+	case VESA_POWERDOWN:
+	case VESA_VSYNC_SUSPEND:
+	case VESA_HSYNC_SUSPEND:
+		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
+		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
+			for (i = 0; i < fbi->palette_size; i++)
+				pxafb_setpalettereg(i, 0, 0, 0, 0, info);
+		pxafb_schedule_work(fbi, C_DISABLE);
+		if (pxafb_blank_helper)
+			pxafb_blank_helper(blank);
+		break;
+
+	case VESA_NO_BLANKING:
+		if (pxafb_blank_helper)
+			pxafb_blank_helper(blank);
+		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
+		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
+			fb_set_cmap(&fbi->fb.cmap, 1, info);
+		pxafb_schedule_work(fbi, C_ENABLE);
+	}
+	return 0;
+}
+
+static struct fb_ops pxafb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= pxafb_check_var,
+	.fb_set_par	= pxafb_set_par,
+//	.fb_set_cmap	= pxafb_set_cmap,
+	.fb_setcolreg	= pxafb_setcolreg,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_blank	= pxafb_blank,
+	.fb_cursor	= soft_cursor,
+};
+
+/*
+ * Calculate the PCD value from the clock rate (in picoseconds).
+ * We take account of the PPCR clock setting.
+ */
+static inline unsigned int get_pcd(unsigned int pixclock, unsigned int cpuclock)
+{
+	unsigned int pcd;
+
+	if (pixclock) {
+		pcd = cpuclock / 100 * pixclock;
+		pcd /= 100000000;
+		pcd += 1;	/* make up for integer math truncations */
+	} else {
+		printk(KERN_WARNING "Please convert me to use the PCD calculations\n");
+		pcd = 0;
+	}
+	return pcd;
+}
+
+/*
+ * pxafb_activate_var():
+ *	Configures LCD Controller based on entries in var parameter.  Settings are      
+ *      only written to the controller if changes were made.  
+ */
+static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi)
+{
+	struct pxafb_lcd_reg new_regs;
+	u_int pcd = get_pcd(var->pixclock, get_clk_frequency_khz(0));
+	u_long flags;
+
+	DPRINTK("Configuring PXA LCD\n");
+
+	DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n",
+		var->xres, var->hsync_len,
+		var->left_margin, var->right_margin);
+	DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n",
+		var->yres, var->vsync_len,
+		var->upper_margin, var->lower_margin);
+
+#if DEBUG_VAR
+	if (var->xres < 16        || var->xres > 1024)
+		printk(KERN_ERR "%s: invalid xres %d\n",
+			fbi->fb.fix.id, var->xres);
+	if (var->hsync_len < 1    || var->hsync_len > 64)
+		printk(KERN_ERR "%s: invalid hsync_len %d\n",
+			fbi->fb.fix.id, var->hsync_len);
+	if (var->left_margin < 1  || var->left_margin > 255)
+		printk(KERN_ERR "%s: invalid left_margin %d\n",
+			fbi->fb.fix.id, var->left_margin);
+	if (var->right_margin < 1 || var->right_margin > 255)
+		printk(KERN_ERR "%s: invalid right_margin %d\n",
+			fbi->fb.fix.id, var->right_margin);
+	if (var->yres < 1         || var->yres > 1024)
+		printk(KERN_ERR "%s: invalid yres %d\n",
+			fbi->fb.fix.id, var->yres);
+	if (var->vsync_len < 1    || var->vsync_len > 64)
+		printk(KERN_ERR "%s: invalid vsync_len %d\n",
+			fbi->fb.fix.id, var->vsync_len);
+	if (var->upper_margin < 0 || var->upper_margin > 255)
+		printk(KERN_ERR "%s: invalid upper_margin %d\n",
+			fbi->fb.fix.id, var->upper_margin);
+	if (var->lower_margin < 0 || var->lower_margin > 255)
+		printk(KERN_ERR "%s: invalid lower_margin %d\n",
+			fbi->fb.fix.id, var->lower_margin);
+#endif
+
+#if defined (CONFIG_PXA_CERF_PDA) || defined(CONFIG_ARCH_H3900) || defined (CONFIG_ARCH_H1900)
+	new_regs.lccr0 = fbi->lccr0;
+	new_regs.lccr1 =
+		LCCR1_DisWdth(var->xres) +
+		LCCR1_HorSnchWdth(var->hsync_len) +
+		LCCR1_BegLnDel(var->left_margin) +
+		LCCR1_EndLnDel(var->right_margin);
+		
+	new_regs.lccr2 =
+		LCCR2_DisHght(var->yres) +
+		LCCR2_VrtSnchWdth(var->vsync_len) +
+		LCCR2_BegFrmDel(var->upper_margin) +
+		LCCR2_EndFrmDel(var->lower_margin);
+
+	new_regs.lccr3 = fbi->lccr3
+		|
+		(var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
+		(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+#elif defined (CONFIG_FB_PXA_QVGA)
+	new_regs.lccr0 = fbi->lccr0;
+	new_regs.lccr1 =
+		LCCR1_DisWdth(var->xres) +
+		LCCR1_HorSnchWdth(var->hsync_len) +
+		LCCR1_BegLnDel(var->left_margin) +
+		LCCR1_EndLnDel(var->right_margin);
+	new_regs.lccr2 =
+		LCCR2_DisHght(var->yres) +
+		LCCR2_VrtSnchWdth(var->vsync_len) +
+		LCCR2_BegFrmDel(var->upper_margin) +
+		LCCR2_EndFrmDel(var->lower_margin);
+	new_regs.lccr3 = fbi->lccr3;
+#else
+	// FIXME using hardcoded values for now
+	new_regs.lccr0 = fbi->lccr0;
+//		|
+//		LCCR0_LEN | LCCR0_LDM | LCCR0_BAM |
+//		LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0);
+
+	new_regs.lccr1 = 0x3030A7F;
+//		LCCR1_DisWdth(var->xres) +
+//		LCCR1_HorSnchWdth(var->hsync_len) +
+//		LCCR1_BegLnDel(var->left_margin) +
+//		LCCR1_EndLnDel(var->right_margin);
+
+	new_regs.lccr2 = 0x4EF;
+//		LCCR2_DisHght(var->yres) +
+//		LCCR2_VrtSnchWdth(var->vsync_len) +
+//		LCCR2_BegFrmDel(var->upper_margin) +
+//		LCCR2_EndFrmDel(var->lower_margin);
+
+	new_regs.lccr3 = fbi->lccr3;
+//	|
+//		(var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
+//		(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL) |
+//		LCCR3_ACBsCntOff;
+#endif
+
+	if (pcd)
+		new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);
+
+	DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
+	DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
+	DPRINTK("nlccr2 = 0x%08x\n", new_regs.lccr2);
+	DPRINTK("nlccr3 = 0x%08x\n", new_regs.lccr3);
+
+	/* Update shadow copy atomically */
+	local_irq_save(flags);
+
+	/* setup dma descriptors */
+	fbi->dmadesc_fblow_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 3*16);
+	fbi->dmadesc_fbhigh_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 2*16);
+	fbi->dmadesc_palette_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 1*16);
+
+	fbi->dmadesc_fblow_dma = fbi->palette_dma - 3*16;
+	fbi->dmadesc_fbhigh_dma = fbi->palette_dma - 2*16;
+	fbi->dmadesc_palette_dma = fbi->palette_dma - 1*16;
+
+	#define BYTES_PER_PANEL	((fbi->lccr0 & LCCR0_SDS) ? (var->xres * var->yres * var->bits_per_pixel / 8 / 2) : \
+			                                    (var->xres * var->yres * var->bits_per_pixel / 8))
+	
+	/* populate descriptors */
+	fbi->dmadesc_fblow_cpu->fdadr = fbi->dmadesc_fblow_dma;
+	fbi->dmadesc_fblow_cpu->fsadr = fbi->screen_dma + BYTES_PER_PANEL;
+	fbi->dmadesc_fblow_cpu->fidr  = 0;
+	fbi->dmadesc_fblow_cpu->ldcmd = BYTES_PER_PANEL;
+
+	fbi->fdadr1 = fbi->dmadesc_fblow_dma; /* only used in dual-panel mode */
+		
+	fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma;
+	fbi->dmadesc_fbhigh_cpu->fidr = 0;
+	fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL;
+
+	fbi->dmadesc_palette_cpu->fsadr = fbi->palette_dma;
+	fbi->dmadesc_palette_cpu->fidr  = 0;
+	fbi->dmadesc_palette_cpu->ldcmd = (fbi->palette_size * 2) | LDCMD_PAL;
+
+	if( var->bits_per_pixel < 12)
+	{
+		/* assume any mode with <12 bpp is palette driven */
+		fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
+		fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_palette_dma;
+		fbi->fdadr0 = fbi->dmadesc_palette_dma; /* flips back and forth between pal and fbhigh */
+	}
+	else
+	{
+		/* palette shouldn't be loaded in true-color mode */
+		fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
+		fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */
+	}
+
+	DPRINTK("fbi->dmadesc_fblow_cpu = 0x%x\n", (unsigned int)fbi->dmadesc_fblow_cpu);
+	DPRINTK("fbi->dmadesc_fbhigh_cpu = 0x%x\n", (unsigned int)fbi->dmadesc_fbhigh_cpu);
+	DPRINTK("fbi->dmadesc_palette_cpu = 0x%x\n", (unsigned int)fbi->dmadesc_palette_cpu);
+	DPRINTK("fbi->dmadesc_fblow_dma = 0x%x\n", fbi->dmadesc_fblow_dma);
+	DPRINTK("fbi->dmadesc_fbhigh_dma = 0x%x\n", fbi->dmadesc_fbhigh_dma);
+	DPRINTK("fbi->dmadesc_palette_dma = 0x%x\n", fbi->dmadesc_palette_dma);
+
+	DPRINTK("fbi->dmadesc_fblow_cpu->fdadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fdadr);
+	DPRINTK("fbi->dmadesc_fbhigh_cpu->fdadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fdadr);
+	DPRINTK("fbi->dmadesc_palette_cpu->fdadr = 0x%x\n", fbi->dmadesc_palette_cpu->fdadr);
+
+	DPRINTK("fbi->dmadesc_fblow_cpu->fsadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fsadr);
+	DPRINTK("fbi->dmadesc_fbhigh_cpu->fsadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fsadr);
+	DPRINTK("fbi->dmadesc_palette_cpu->fsadr = 0x%x\n", fbi->dmadesc_palette_cpu->fsadr);
+
+	DPRINTK("fbi->dmadesc_fblow_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fblow_cpu->ldcmd);
+	DPRINTK("fbi->dmadesc_fbhigh_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fbhigh_cpu->ldcmd);
+	DPRINTK("fbi->dmadesc_palette_cpu->ldcmd = 0x%x\n", fbi->dmadesc_palette_cpu->ldcmd);
+	
+	fbi->reg_lccr0 = new_regs.lccr0;
+	fbi->reg_lccr1 = new_regs.lccr1;
+	fbi->reg_lccr2 = new_regs.lccr2;
+	fbi->reg_lccr3 = new_regs.lccr3;
+	local_irq_restore(flags);
+
+	/*
+	 * Only update the registers if the controller is enabled
+	 * and something has changed.
+	 */
+	if ((LCCR0 != fbi->reg_lccr0)       || (LCCR1 != fbi->reg_lccr1) ||
+	    (LCCR2 != fbi->reg_lccr2)       || (LCCR3 != fbi->reg_lccr3) ||
+	    (FDADR0 != fbi->fdadr0) || (FDADR1 != fbi->fdadr1))
+		pxafb_schedule_work(fbi, C_REENABLE);
+
+	return 0;
+}
+
+/*
+ * NOTE!  The following functions are purely helpers for set_ctrlr_state.
+ * Do not call them directly; set_ctrlr_state does the correct serialisation
+ * to ensure that things happen in the right way 100% of time time.
+ *	-- rmk
+ */
+
+/*
+ * FIXME: move LCD power stuff into pxafb_power_up_lcd()
+ * Also, I'm expecting that the backlight stuff should
+ * be handled differently.
+ */
+static inline void __pxafb_backlight_on(struct pxafb_info *fbi, int on)
+{
+	DPRINTK("backlight o%s\n", on ? "n" : "ff");
+
+#if defined(CONFIG_IPAQ_HANDHELD) && !CONFIG_LCD_MODULE
+	if (machine_is_ipaq ())
+	{
+		DPRINTK("Using iPAQ backlight\n");
+		ipaq_backlight_power (on);
+	}
+#endif
+#ifdef CONFIG_LCD_MODULE
+	if (1) { 
+		struct backlight_module *bl = backlight_module_get("pxafb");
+		if (bl && bl->set_power)
+		{
+			bl->set_power(bl, on);
+		}
+		backlight_module_put(bl);
+	}
+#endif
+	if (pxafb_backlight_power)
+		pxafb_backlight_power(on);
+}
+
+static inline void __pxafb_lcd_enable(struct pxafb_info *fbi, int on)
+{
+	DPRINTK("LCD %sable\n", "on" ? "en" : "dis");
+
+#ifdef CONFIG_LCD_MODULE
+	{ 
+		struct lcd_module *lcd = lcd_module_get("pxafb");
+		if (lcd && lcd->set_enable)
+		{
+			lcd->set_enable(lcd, on);
+		}
+		lcd_module_put(lcd);
+	}
+#endif
+#if defined(CONFIG_IPAQ_HANDHELD) && !defined(CONFIG_LCD_MODULE)
+	if (machine_is_ipaq()) {
+		DPRINTK("Using iPAQ enabler\n");
+		if (on)
+			set_h3600_egpio( IPAQ_EGPIO_LCD_ENABLE );
+		else
+			clr_h3600_egpio( IPAQ_EGPIO_LCD_ENABLE );
+	}
+#endif
+}
+
+/*
+ * FIXME: move LCD power stuf into pxafb_power_down_lcd()
+ * Also, I'm expecting that the backlight stuff should
+ * be handled differently.
+ */
+static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on)
+{
+	DPRINTK("LCD power o%s\n", on ? "n" : "ff");
+	CKEN |= CKEN16_LCD;
+
+#if defined(CONFIG_IPAQ_HANDHELD) && !defined(CONFIG_LCD_MODULE)
+	if (machine_is_ipaq ())
+	{
+		if (on)
+			set_h3600_egpio( IPAQ_EGPIO_LCD_POWER );
+		else
+			clr_h3600_egpio( IPAQ_EGPIO_LCD_POWER );
+	};
+#endif
+#ifdef CONFIG_LCD_MODULE
+	{ 
+		struct lcd_module *lm = lcd_module_get("pxafb");
+		if (lm && lm->set_power)
+			lm->set_power(lm, on);
+		lcd_module_put(lm);
+	}
+#endif
+	if (pxafb_lcd_power)
+		pxafb_lcd_power(on);
+}
+
+static void pxafb_setup_gpio(struct pxafb_info *fbi)
+{
+	unsigned int lccr0;
+
+	/*
+	 * setup is based on type of panel supported
+	 */
+
+	lccr0 = fbi->lccr0;
+
+	/* 4 bit interface */
+	if ((lccr0 & LCCR0_CMS) && (lccr0 & LCCR0_SDS) && !(lccr0 & LCCR0_DPD))
+	{
+		// bits 58-61
+		GPDR1 |= (0xf << 26);
+		GAFR1_U = (GAFR1_U & ~(0xff << 20)) | (0xaa << 20);
+
+		// bits 74-77
+		GPDR2 |= (0xf << 10);
+                GAFR2_L = (GAFR2_L & ~(0xff << 20)) | (0xaa << 20);
+	}
+
+        /* 8 bit interface */
+        else if (((lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_DPD))) ||
+                 (!(lccr0 & LCCR0_CMS) && !(lccr0 & LCCR0_PAS) && !(lccr0 & LCCR0_SDS)))
+        {
+		// bits 58-65
+                GPDR1 |= (0x3f << 26);
+                GPDR2 |= (0x3);
+
+                GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20);
+                GAFR2_L = (GAFR2_L & ~0xf) | (0xa);
+
+                // bits 74-77
+                GPDR2 |= (0xf << 10);
+                GAFR2_L = (GAFR2_L & ~(0xff << 20)) | (0xaa << 20);
+        }
+
+        /* 16 bit interface */
+        else if (!(lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_PAS)))
+        {
+		// bits 58-77
+                GPDR1 |= (0x3f << 26);
+                GPDR2 |= 0x00003fff;
+
+                GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20);
+                GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa;
+        }
+	else
+	{
+		printk( KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n");
+	}
+}
+
+static void pxafb_enable_controller(struct pxafb_info *fbi)
+{
+	DPRINTK("Enabling LCD controller\n");
+
+	/* Sequence from 11.7.10 */
+	LCCR3 = fbi->reg_lccr3;
+	LCCR2 = fbi->reg_lccr2;
+	LCCR1 = fbi->reg_lccr1;
+	LCCR0 = fbi->reg_lccr0 & ~LCCR0_ENB;
+
+	/* FIXME we used to have LCD power control here */
+
+	FDADR0 = fbi->fdadr0;
+	FDADR1 = fbi->fdadr1;
+	LCCR0 |= LCCR0_ENB;
+
+	DPRINTK("FDADR0 = 0x%08x\n", (unsigned int)FDADR0);
+	DPRINTK("FDADR1 = 0x%08x\n", (unsigned int)FDADR1);
+	DPRINTK("LCCR0 = 0x%08x\n", (unsigned int)LCCR0);
+	DPRINTK("LCCR1 = 0x%08x\n", (unsigned int)LCCR1);
+	DPRINTK("LCCR2 = 0x%08x\n", (unsigned int)LCCR2);
+	DPRINTK("LCCR3 = 0x%08x\n", (unsigned int)LCCR3);
+}
+
+static void pxafb_disable_controller(struct pxafb_info *fbi)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	DPRINTK("Disabling LCD controller\n");
+
+	/* FIXME add power down GPIO stuff here */
+
+	add_wait_queue(&fbi->ctrlr_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	LCSR = 0xffffffff;	/* Clear LCD Status Register */
+	LCCR0 &= ~LCCR0_LDM;	/* Enable LCD Disable Done Interrupt */
+	if (0) enable_irq(IRQ_LCD);	/* Enable LCD IRQ */
+	LCCR0 &= ~LCCR0_ENB;	/* Disable LCD Controller */
+
+	schedule_timeout(20 * HZ / 1000);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&fbi->ctrlr_wait, &wait);
+}
+
+/*
+ *  pxafb_handle_irq: Handle 'LCD DONE' interrupts.
+ */
+static irqreturn_t pxafb_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct pxafb_info *fbi = dev_id;
+	unsigned int lcsr = LCSR;
+
+	if (lcsr & LCSR_LDD) {
+		LCCR0 |= LCCR0_LDM;
+		wake_up(&fbi->ctrlr_wait);
+	}
+
+	LCSR = lcsr;
+	return IRQ_HANDLED;
+}
+
+/*
+ * This function must be called from task context only, since it will
+ * sleep when disabling the LCD controller, or if we get two contending
+ * processes trying to alter state.
+ */
+static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
+{
+	u_int old_state;
+
+	down(&fbi->ctrlr_sem);
+
+	old_state = fbi->state;
+	
+	/*
+	 * Hack around fbcon initialization.
+	 */
+	if (old_state == C_STARTUP && state == C_REENABLE)
+		state = C_ENABLE;
+
+	switch (state) {
+	case C_DISABLE_CLKCHANGE:
+		/*
+		 * Disable controller for clock change.  If the
+		 * controller is already disabled, then do nothing.
+		 */
+		if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {
+			fbi->state = state;
+			__pxafb_lcd_enable(fbi, 0);
+			__pxafb_lcd_power(fbi, 0);
+			pxafb_disable_controller(fbi);
+		}
+		break;
+
+	case C_DISABLE_PM:
+	case C_DISABLE:
+		/*
+		 * Disable controller
+		 */
+		if (old_state != C_DISABLE) {
+			printk("Disabling LCD\n");
+			fbi->state = state;
+
+			__pxafb_backlight_on(fbi, 0);
+			__pxafb_lcd_enable(fbi, 0);
+			__pxafb_lcd_power(fbi, 0);
+			if (old_state != C_DISABLE_CLKCHANGE)
+				pxafb_disable_controller(fbi);
+		}
+		break;
+
+	case C_ENABLE_CLKCHANGE:
+		/*
+		 * Enable the controller after clock change.  Only
+		 * do this if we were disabled for the clock change.
+		 */
+		if (old_state == C_DISABLE_CLKCHANGE) {
+			fbi->state = C_ENABLE;
+			pxafb_enable_controller(fbi);
+			__pxafb_lcd_power(fbi, 1);
+			__pxafb_lcd_enable(fbi, 1);
+		}
+		break;
+
+	case C_REENABLE:
+		/*
+		 * Re-enable the controller only if it was already
+		 * enabled.  This is so we reprogram the control
+		 * registers.
+		 */
+		if (old_state == C_ENABLE) {
+			pxafb_disable_controller(fbi);
+			pxafb_setup_gpio(fbi);
+			pxafb_enable_controller(fbi);
+		}
+		break;
+
+	case C_ENABLE_PM:
+		/*
+		 * Re-enable the controller after PM.  This is not
+		 * perfect - think about the case where we were doing
+		 * a clock change, and we suspended half-way through.
+		 */
+		if (old_state != C_DISABLE_PM)
+			break;
+		/* fall through */
+
+	case C_ENABLE:
+		/*
+		 * Power up the LCD screen, enable controller, and
+		 * turn on the backlight.
+		 */
+		if (old_state != C_ENABLE) {
+			fbi->state = C_ENABLE;
+			printk("Enabling LCD\n");
+			pxafb_setup_gpio(fbi);
+			pxafb_enable_controller(fbi);
+			__pxafb_lcd_power(fbi, 1);
+			__pxafb_lcd_enable(fbi, 1);
+			__pxafb_backlight_on(fbi, 1);
+		}
+		break;
+	}
+	up(&fbi->ctrlr_sem);
+}
+
+/*
+ * Our LCD controller task (which is called when we blank or unblank)
+ * via keventd.
+ */
+static void pxafb_task(void *dummy)
+{
+	struct pxafb_info *fbi = dummy;
+	u_int state = xchg(&fbi->task_state, -1);
+
+	set_ctrlr_state(fbi, state);
+}
+
+#ifdef CONFIG_CPU_FREQ
+/*
+ * CPU clock speed change handler.  We need to adjust the LCD timing
+ * parameters when the CPU clock is adjusted by the power management
+ * subsystem.
+ */
+static int
+pxafb_freq_transition(struct notifier_block *nb, unsigned long val,
+			 void *data)
+{
+	struct pxafb_info *fbi = TO_INF(nb, freq_transition);
+	struct cpufreq_freqs *f = data;
+	u_int pcd;
+
+	switch (val) {
+	case CPUFREQ_PRECHANGE:
+		set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
+		break;
+
+	case CPUFREQ_POSTCHANGE:
+		pcd = get_pcd(fbi->fb.var.pixclock, f->new);
+		fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
+		set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
+		break;
+	}
+	return 0;
+}
+
+static int
+sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val,
+		     void *data)
+{
+	struct pxafb_info *fbi = TO_INF(nb, freq_policy);
+	struct cpufreq_policy *policy = data;
+
+	switch (val) {
+	case CPUFREQ_ADJUST:
+	case CPUFREQ_INCOMPATIBLE:
+		break;
+	case CPUFREQ_NOTIFY:
+		do {} while(0);
+		/* todo: panic if min/max values aren't fulfilled 
+		 * [can't really happen unless there's a bug in the
+		 * CPU policy verififcation process *
+		 */
+		break;
+	}
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+/*
+ * Power management hooks.  Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+static int pxafb_suspend(struct device *dev, u32 state, u32 level)
+{
+	struct pxafb_info *fbi = dev_get_drvdata(dev);
+
+	if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN)
+		set_ctrlr_state(fbi, C_DISABLE_PM);
+	return 0;
+}
+
+static int pxafb_resume(struct device *dev, u32 level)	
+{
+	struct pxafb_info *fbi = dev_get_drvdata(dev);
+	if (level == RESUME_ENABLE)
+		set_ctrlr_state(fbi, C_ENABLE_PM);
+	return 0;
+}
+#else
+#define pxafb_suspend		NULL
+#define pxafb_resume		NULL
+#endif
+
+/*
+ * pxafb_map_video_memory():
+ *      Allocates the DRAM memory for the frame buffer.  This buffer is  
+ *	remapped into a non-cached, non-buffered, memory region to  
+ *      allow palette and pixel writes to occur without flushing the 
+ *      cache.  Once this area is remapped, all virtual memory
+ *      access to the video memory should occur at the new region.
+ */
+static int __init pxafb_map_video_memory(struct pxafb_info *fbi)
+{
+	u_long palette_mem_size;
+
+	/*
+	 * We reserve one page for the palette, plus the size
+	 * of the framebuffer.
+	 *
+	 * layout of stuff in memory
+	 *
+	 *                fblow descriptor
+	 *                fbhigh descriptor
+	 *                palette descriptor
+	 *                palette
+	 *   page boundary->
+	 *                frame buffer
+	 */
+	fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
+	fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size,
+					&fbi->map_dma, PTE_BUFFERABLE);
+
+	if (fbi->map_cpu) {
+		fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE;
+		fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
+		fbi->fb.fix.smem_start = fbi->screen_dma;
+
+		fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16;
+
+		palette_mem_size = fbi->palette_size * sizeof(u16);
+
+		DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
+
+		fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
+		fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
+
+	}
+
+	return fbi->map_cpu ? 0 : -ENOMEM;
+}
+
+/* Fake monspecs to fill in fbinfo structure */
+static struct fb_monspecs monspecs __initdata = {
+	30000, 70000, 50, 65, 0	/* Generic */
+};
+
+
+static struct pxafb_info * __init pxafb_init_fbinfo(void)
+{
+	struct pxafb_mach_info *inf;
+	struct pxafb_info *fbi;
+
+	fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16,
+		      GFP_KERNEL);
+	if (!fbi)
+		return NULL;
+
+	memset(fbi, 0, sizeof(struct pxafb_info) + sizeof(struct pxafb_info));
+
+	strcpy(fbi->fb.fix.id, PXA_NAME);
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.type_aux	= 0;
+	fbi->fb.fix.xpanstep	= 0;
+	fbi->fb.fix.ypanstep	= 0;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.var.nonstd	= 0;
+	fbi->fb.var.activate	= FB_ACTIVATE_NOW;
+	fbi->fb.var.height	= -1;
+	fbi->fb.var.width	= -1;
+	fbi->fb.var.accel_flags	= 0;
+	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;
+
+	fbi->fb.fbops		= &pxafb_ops;
+	fbi->fb.flags		= FBINFO_FLAG_DEFAULT;
+	fbi->fb.monspecs	= monspecs;
+	fbi->fb.currcon		= -1;
+	fbi->fb.pseudo_palette	= (fbi + 1);
+
+	fbi->rgb[RGB_8]		= &rgb_8;
+	fbi->rgb[RGB_16]	= &def_rgb_16;
+
+	inf = pxafb_get_machine_info(fbi);
+	printk("inf=%p inf->xres=%d inf->yres=%d inf->bpp=%d\n", inf, inf->xres, inf->yres, inf->bpp);
+
+#ifdef YES_I_GET_IT_PLEASE_WHINE_AT_ME	
+	/*
+	 * People just don't seem to get this.  We don't support
+	 * anything but correct entries now, so panic if someone
+	 * does something stupid.
+	 */
+	if (inf->lccr3 & (LCCR3_VrtSnchL|LCCR3_HorSnchL|0xff) ||
+	    inf->pixclock == 0)
+		panic("pxafb error: invalid LCCR3 fields set or zero "
+			"pixclock.");
+#endif
+
+	fbi->max_xres			= inf->xres;
+	fbi->fb.var.xres		= inf->xres;
+	fbi->fb.var.xres_virtual	= inf->xres;
+	fbi->max_yres			= inf->yres;
+	fbi->fb.var.yres		= inf->yres;
+	fbi->fb.var.yres_virtual	= inf->yres;
+	fbi->max_bpp			= inf->bpp;
+	fbi->fb.var.bits_per_pixel	= inf->bpp;
+	fbi->fb.var.pixclock		= inf->pixclock;
+	fbi->fb.var.hsync_len		= inf->hsync_len;
+	fbi->fb.var.left_margin		= inf->left_margin;
+	fbi->fb.var.right_margin	= inf->right_margin;
+	fbi->fb.var.vsync_len		= inf->vsync_len;
+	fbi->fb.var.upper_margin	= inf->upper_margin;
+	fbi->fb.var.lower_margin	= inf->lower_margin;
+	fbi->fb.var.sync		= inf->sync;
+	fbi->fb.var.grayscale		= inf->cmap_greyscale;
+	fbi->cmap_inverse		= inf->cmap_inverse;
+	fbi->cmap_static		= inf->cmap_static;
+	fbi->lccr0			= inf->lccr0;
+	fbi->lccr3			= inf->lccr3;
+	fbi->state			= C_STARTUP;
+	fbi->task_state			= (u_char)-1;
+	fbi->fb.fix.smem_len		= fbi->max_xres * fbi->max_yres *
+					  fbi->max_bpp / 8;
+
+	init_waitqueue_head(&fbi->ctrlr_wait);
+	INIT_WORK(&fbi->task, pxafb_task, fbi);
+	init_MUTEX(&fbi->ctrlr_sem);
+
+	return fbi;
+}
+
+static int __init pxafb_probe(struct device *dev)
+{
+	struct pxafb_info *fbi;
+	int ret;
+	
+	printk("About to attempt to initialize pxafb\n");
+	
+	/* H5400 LCD is connected to the MediaQ chip instead.  */
+	if (machine_is_h5400 ())
+		return -ENODEV;
+		
+	if (!request_mem_region(0x44000000, 0x10000, "LCD"))
+		return -EBUSY;
+
+	fbi = pxafb_init_fbinfo();
+	ret = -ENOMEM;
+	printk("pxafb_probe: fbi=%p\n", fbi);
+	if (!fbi)
+		goto failed;
+
+	/* Initialize video memory */
+	ret = pxafb_map_video_memory(fbi);
+	printk("pxafb_map_video_memory returned %d\n", ret);
+	if (ret)
+		goto failed;
+
+	ret = request_irq(IRQ_LCD, pxafb_handle_irq, SA_INTERRUPT,
+			  "LCD", fbi);
+	if (ret) {
+		printk(KERN_ERR "pxafb: failed in request_irq: %d\n", ret);
+		goto failed;
+	}
+
+	/*
+	 * This makes sure that our colour bitfield
+	 * descriptors are correctly initialised.
+	 */
+	pxafb_check_var(&fbi->fb.var, &fbi->fb);
+	
+	dev_set_drvdata(dev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	printk("register_framebuffer returned %d\n", ret);
+	if (ret < 0)
+		goto failed;
+
+#ifdef CONFIG_CPU_FREQ
+	fbi->freq_transition.notifier_call = pxafb_freq_transition;
+	fbi->freq_policy.notifier_call = pxafb_freq_policy;
+	cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
+	cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);
+#endif
+
+	/* This driver cannot be unloaded at the moment */
+	try_module_get(THIS_MODULE);
+
+	printk("Initialized pxafb\n");
+	return 0;
+
+failed:
+	dev_set_drvdata(dev, NULL);
+	if (fbi)
+		kfree(fbi);
+	release_mem_region(0x44000000, 0x10000);
+	printk("Failed to initialize pxafb\n");
+	return ret;
+}
+
+static struct device_driver pxafb_driver = {
+	.name		= "pxa-fb",
+	.bus		= &platform_bus_type,
+	.probe		= pxafb_probe,
+	.suspend	= pxafb_suspend,
+	.resume		= pxafb_resume,
+};
+
+int __init pxafb_init(void)
+{
+	return driver_register(&pxafb_driver);
+}
+
+
+#ifdef MODULE
+module_init(pxafb_init);
+#endif
+
+MODULE_DESCRIPTION("loadable framebuffer driver for PXA");
+MODULE_LICENSE("GPL");
--- linux-2.6.0-test8-rmk1/arch/arm/mach-pxa/h3900.c	1969-12-31 19:00:00.000000000 -0500
+++ kernel26/arch/arm/mach-pxa/h3900.c	2003-11-04 13:09:33.000000000 -0500
@@ -0,0 +1,683 @@
+/*
+ * Hardware definitions for HP iPAQ Handheld Computers
+ *
+ * Copyright 2000-2003 Hewlett-Packard Company.
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Author: Jamey Hicks.
+ *
+ * History:
+ *
+ * 2003-05-14	Joshua Wise        Adapted for the HP iPAQ H1900
+ * 2002-08-23   Jamey Hicks        Adapted for use with PXA250-based iPAQs
+ * 2001-10-??   Andrew Christian   Added support for iPAQ H3800
+ *                                 and abstracted EGPIO interface.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bootmem.h>
+#include <linux/lcd-module.h>
+#include <linux/fb.h>
+#include <../drivers/video/pxafb.h>
+
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/setup.h>
+
+#include <asm/mach/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/arch/h3900-init.h>
+#include <asm/arch/h3900_asic.h>
+#include <asm/arch/h3900-irqs.h>
+#include <asm/arch/ipaq.h>
+
+#include <asm/arch/irq.h>
+#include <asm/types.h>
+
+#include <linux/serial_core.h>
+
+#include "generic.h"
+
+#define SET_ASIC3(x) \
+   do {if ( setp ) { H3900_ASIC3_GPIO_B_OUT |= (x); } else { H3900_ASIC3_GPIO_B_OUT &= ~(x); }} while(0)
+
+#define SET_ASIC2(x) \
+   do {if ( setp ) { H3800_ASIC2_GPIOPIOD |= (x); } else { H3800_ASIC2_GPIOPIOD &= ~(x); }} while(0)
+
+#define CLEAR_ASIC3(x) \
+   do {if ( setp ) { H3900_ASIC3_GPIO_B_OUT &= ~(x); } else { H3900_ASIC3_GPIO_B_OUT |= (x); }} while(0)
+
+#define CLEAR_ASIC2(x) \
+   do {if ( setp ) { H3800_ASIC2_B_GPIOPIOD &= ~(x); } else { H3800_ASIC2_B_GPIOPIOD |= (x); }} while(0)
+
+
+/*
+  On screen enable, we get 
+  
+     h3800_lcd_power_on(1)
+     LCD controller starts
+     h3800_lcd_enable(1)
+
+  On screen disable, we get
+  
+     h3800_lcd_enable(0)
+     LCD controller stops
+     h3800_lcd_power_on(0)
+*/
+
+static int h3900_lcd_set_power( struct lcd_module *lm, int setp )
+{
+	if ( setp ) {
+		H3900_ASIC3_GPIO_B_OUT |= GPIO3_LCD_ON;
+		mdelay(30);
+		H3900_ASIC3_GPIO_B_OUT |= GPIO3_LCD_NV_ON;
+		mdelay(5);
+		H3900_ASIC3_GPIO_B_OUT |= GPIO3_LCD_9V_ON;
+		mdelay(50);
+		H3900_ASIC3_GPIO_B_OUT |= GPIO3_LCD_5V_ON;
+		mdelay(5);
+	} else {
+		mdelay(5);
+		H3900_ASIC3_GPIO_B_OUT &= ~GPIO3_LCD_5V_ON;
+		mdelay(50);
+		H3900_ASIC3_GPIO_B_OUT &= ~GPIO3_LCD_9V_ON;
+		mdelay(5);
+		H3900_ASIC3_GPIO_B_OUT &= ~GPIO3_LCD_NV_ON;
+		mdelay(100);
+		H3900_ASIC3_GPIO_B_OUT &= ~GPIO3_LCD_ON;
+	}
+	return 0;
+}
+
+static int h3900_lcd_get_power( struct lcd_module *lm, int *psetp )
+{
+	if (psetp) {
+		if (H3900_ASIC3_GPIO_B_OUT & GPIO3_LCD_ON)
+			*psetp = 1;
+		else
+			*psetp = 0;
+	}
+	return 0;
+}
+
+static int h3900_lcd_set_enable( struct lcd_module *lm, int setp )
+{
+	if ( setp ) {
+		mdelay(17);     // Wait one from before turning on
+		H3900_ASIC3_GPIO_B_OUT |= GPIO3_LCD_PCI;
+	} else {
+		H3900_ASIC3_GPIO_B_OUT &= ~GPIO3_LCD_PCI;
+		mdelay(30);     // Wait before turning off
+	}
+	return 0;
+}
+
+static int h3900_lcd_get_enable( struct lcd_module *lm, int *psetp )
+{
+	if (psetp) {
+		if (H3900_ASIC3_GPIO_B_OUT & GPIO3_LCD_PCI)
+			*psetp = 1;
+		else
+			*psetp = 0;
+	}
+	return 0;
+}
+
+/* read LCCR0: 0x00000081 */
+/* read LCCR1: 0x0b10093f -- BLW=0x0b, ELW=0x10, HSW=2, PPL=0x13f */
+/* read LCCR2: 0x051008ef -- BFW=0x05, EFW=0x10, VSW=2, LPP=0xef */
+/* read LCCR3: 0x0430000a */
+static struct pxafb_mach_info h3900_fb_info __initdata = {
+	.pixclock =	221039,	/* clock period in ps */
+	.bpp =		16,
+	.xres =		320,
+	.yres =		240,
+	.hsync_len =	3,
+	.vsync_len =	3,
+	.left_margin =	12,
+	.upper_margin =	6,
+	.right_margin =	17,
+	.lower_margin =	17,
+	.sync =		0, /* both horiz and vert active low sync */
+	.lccr0 =	(LCCR0_PAS | LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM  | LCCR0_OUM),
+	.lccr3 =	(LCCR3_16BPP)
+};
+
+static int h3900_get_mach_info(struct lcd_module *lm, void **mach_info)
+{
+	*(struct pxafb_mach_info **)mach_info = &h3900_fb_info;
+	return 0;
+}
+
+static struct lcd_module h3900_lcd_module = {
+	.name          = "pxafb",
+	.get_mach_info = h3900_get_mach_info,
+	.set_power     = h3900_lcd_set_power,
+	.get_power     = h3900_lcd_get_power,
+	.set_enable    = h3900_lcd_set_enable,
+	.get_enable    = h3900_lcd_get_enable,
+};
+
+static void h3900_control_egpio( enum ipaq_egpio_type x, int setp )
+{
+	switch (x) {
+	case IPAQ_EGPIO_LCD_POWER:
+		h3900_lcd_set_power( NULL, setp );
+		break;
+	case IPAQ_EGPIO_LCD_ENABLE:
+		h3900_lcd_set_enable( NULL, setp );
+		break;
+	case IPAQ_EGPIO_CODEC_NRESET:
+	case IPAQ_EGPIO_AUDIO_ON:
+	case IPAQ_EGPIO_QMUTE:
+		printk("%s: error - should not be called\n", __FUNCTION__);
+		break;
+	case IPAQ_EGPIO_OPT_NVRAM_ON:
+		SET_ASIC2( GPIO2_OPT_ON_NVRAM );
+		break;
+	case IPAQ_EGPIO_OPT_ON:
+		SET_ASIC2( GPIO2_OPT_ON );
+		break;
+	case IPAQ_EGPIO_CARD_RESET:
+		SET_ASIC2( GPIO2_OPT_PCM_RESET );
+		break;
+	case IPAQ_EGPIO_OPT_RESET:
+		SET_ASIC2( GPIO2_OPT_RESET );
+		break;
+	case IPAQ_EGPIO_IR_ON:
+		CLEAR_ASIC3( GPIO3_IR_ON_N );
+		break;
+	case IPAQ_EGPIO_IR_FSEL:
+		break;
+	case IPAQ_EGPIO_RS232_ON:
+		SET_ASIC3( GPIO3_RS232_ON );
+		break;
+	case IPAQ_EGPIO_BLUETOOTH_ON:
+		SET_ASIC3( GPIO3_BT_PWR_ON );
+		break;
+	case IPAQ_EGPIO_VPP_ON:
+		if (setp)
+			GPSR(GPIO_NR_H3900_FLASH_VPEN) = GPIO_H3900_FLASH_VPEN;
+		else
+			GPCR(GPIO_NR_H3900_FLASH_VPEN) = GPIO_H3900_FLASH_VPEN;
+		break;
+	default:
+		printk("%s: unhandled egpio=%d\n", __FUNCTION__, x);
+	}
+}
+
+static unsigned long h3900_read_egpio( enum ipaq_egpio_type x)
+{
+	switch (x) {
+	case IPAQ_EGPIO_PCMCIA_CD0_N:
+		return(GPLR(GPIO_NR_H3900_PCMCIA_CD0_N) & GPIO_bit(GPIO_NR_H3900_PCMCIA_CD0_N));
+	case IPAQ_EGPIO_PCMCIA_CD1_N:
+		return(GPLR(GPIO_NR_H3900_PCMCIA_CD1_N) & GPIO_bit(GPIO_NR_H3900_PCMCIA_CD1_N));
+	case IPAQ_EGPIO_PCMCIA_IRQ0:
+		return(GPLR(GPIO_NR_H3900_PCMCIA_IRQ0_N) & GPIO_bit(GPIO_NR_H3900_PCMCIA_IRQ0_N));
+	case IPAQ_EGPIO_PCMCIA_IRQ1:
+		return(GPLR(GPIO_NR_H3900_PCMCIA_IRQ1_N) & GPIO_bit(GPIO_NR_H3900_PCMCIA_IRQ1_N));
+        default:
+		printk("%s:%d: unknown ipaq_egpio_type=%d\n", __FUNCTION__, __LINE__, x);
+		return 0;
+	}
+}
+
+/* We need to fix ASIC2 GPIO over suspend/resume.  At the moment,
+   it doesn't appear that ASIC3 GPIO has the same problem */
+
+static int h3900_pm_callback( int req )
+{
+	static u16 asic3_data;
+	static u16 asic2_data;
+	int result = 0;
+
+#ifdef DEBUG_PM	
+	printk("%s %d\n", __FUNCTION__, req);
+#endif
+
+	switch (req) {
+	case PM_RESUME:
+		H3800_ASIC2_GPIOPIOD = asic2_data;
+		H3800_ASIC2_GPIODIR = GPIO2_PEN_IRQ 
+			| GPIO2_SD_DETECT
+			| GPIO2_EAR_IN_N 
+			| GPIO2_USB_DETECT_N 
+			| GPIO2_SD_CON_SLT;
+
+		H3900_ASIC3_GPIO_B_OUT = asic3_data;
+
+#ifdef DeadCode
+		if ( ipaq_model_ops.pm_callback_aux )
+			result = ipaq_model_ops.pm_callback_aux(req);
+#endif
+		break;
+
+	case PM_SUSPEND:
+#ifdef DeadCode
+		if ( ipaq_model_ops.pm_callback_aux &&
+		     ((result = ipaq_model_ops.pm_callback_aux(req)) != 0))
+			return result;
+#endif
+
+		asic3_data = H3900_ASIC3_GPIO_B_OUT;
+		asic2_data = H3800_ASIC2_GPIOPIOD;
+		break;
+	default:
+		printk("%s: unrecognized PM callback\n", __FUNCTION__);
+		break;
+	}
+	return result;
+}
+
+
+
+struct egpio_irq_info h3900_egpio_irq_info[] = {
+	{ IPAQ_EGPIO_PCMCIA_CD0_N, GPIO_NR_H3900_PCMCIA_CD0_N, IRQ_GPIO_H3900_PCMCIA_CD0 }, 
+	{ IPAQ_EGPIO_PCMCIA_CD1_N, GPIO_NR_H3900_PCMCIA_CD1_N, IRQ_GPIO_H3900_PCMCIA_CD1 },
+	{ IPAQ_EGPIO_PCMCIA_IRQ0,  GPIO_NR_H3900_PCMCIA_IRQ0_N, IRQ_GPIO_H3900_PCMCIA_IRQ0 },
+	{ IPAQ_EGPIO_PCMCIA_IRQ1,  GPIO_NR_H3900_PCMCIA_IRQ1_N, IRQ_GPIO_H3900_PCMCIA_IRQ1 },
+	{ 0, 0 }
+}; 
+
+static int h3900_egpio_irq_number(enum ipaq_egpio_type egpio_nr)
+{
+	struct egpio_irq_info *info = h3900_egpio_irq_info;
+	while (info->irq != 0) {
+		if (info->egpio_nr == egpio_nr) {
+			if (0) printk("%s: egpio_nr=%d irq=%d\n", __FUNCTION__, egpio_nr, info->irq);
+			return info->irq;
+		}
+		info++;
+	}
+
+	printk("%s: unhandled egpio_nr=%d\n", __FUNCTION__, egpio_nr); 
+	return -EINVAL;
+}
+
+static void h3900_set_led (enum led_color color, int duty_time, int cycle_time)
+{
+	if (duty_time) {
+		H3800_ASIC2_LED_TimeBase(color)   = LEDTBS_BLINK | LEDTBS_AUTOSTOP | LEDTBS_ALWAYS | 1;
+		H3800_ASIC2_LED_PeriodTime(color) = cycle_time;
+		H3800_ASIC2_LED_DutyTime(color)   = duty_time;
+	} else {
+		H3800_ASIC2_LED_TimeBase(color) = 0;
+	}
+}
+
+static int h3900_backlight_set_power (struct backlight_module *bl, int on)
+{
+	if (on) {
+		H3800_ASIC2_CLOCK_Enable    |= ASIC2_CLOCK_PWM | ASIC2_CLOCK_EX1;
+		H3800_ASIC2_PWM_0_DutyTime = 0x100;
+		H3800_ASIC2_PWM_0_PeriodTime = 0x80;
+		H3800_ASIC2_PWM_0_TimeBase = PWM_TIMEBASE_ENABLE | 0x8;
+		H3900_ASIC3_GPIO_B_OUT |= GPIO3_FL_PWR_ON;
+	} else {
+		H3800_ASIC2_PWM_0_TimeBase &= ~PWM_TIMEBASE_ENABLE;
+		H3800_ASIC2_CLOCK_Enable   &= ~ASIC2_CLOCK_PWM;
+		H3900_ASIC3_GPIO_B_OUT &= ~GPIO3_FL_PWR_ON;
+	}
+	return 0;
+}
+
+static int h3900_backlight_get_power (struct backlight_module *bl, int *psetp)
+{
+	if (psetp) {
+		if (H3900_ASIC3_GPIO_B_OUT & GPIO3_FL_PWR_ON)
+			*psetp = 1;
+		else
+			*psetp = 0;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_LCD_MODULE
+static struct backlight_module h3900_backlight_module = {
+	.name          = "pxafb",
+	.set_power     = h3900_backlight_set_power,
+	.get_power     = h3900_backlight_get_power,
+};
+#endif
+
+
+
+static struct ipaq_model_ops h3900_model_ops __initdata = {
+	.generic_name = "3900",
+	.control      = h3900_control_egpio,
+	.read         = h3900_read_egpio,
+#ifdef DeadCode
+	.pm_callback  = h3900_pm_callback,
+#endif
+	.irq_number   = h3900_egpio_irq_number,
+	.set_led      = h3900_set_led,
+#ifndef CONFIG_LCD_MODULE
+	.backlight_power = h3900_backlight_set_power
+#endif
+};
+
+#define MAX_ASIC_ISR_LOOPS    20
+
+/* The order of these is important - see #include <asm/arch/irqs.h> */
+static u32 kpio_irq_mask[] = {
+	KPIO_KEY_ALL,
+	KPIO_SPI_INT,
+	KPIO_OWM_INT,
+	KPIO_ADC_INT,
+	KPIO_UART_0_INT,
+	KPIO_UART_1_INT,
+	KPIO_TIMER_0_INT,
+	KPIO_TIMER_1_INT,
+	KPIO_TIMER_2_INT
+};
+
+static u32 gpio_irq_mask[] = {
+	GPIO2_PEN_IRQ,
+	GPIO2_SD_DETECT,
+	GPIO2_EAR_IN_N,
+	GPIO2_USB_DETECT_N,
+	GPIO2_SD_CON_SLT,
+};
+
+static irqreturn_t h3900_IRQ_demux( int irq, void *dev_id, struct pt_regs *regs )
+{
+	int i;
+
+	if (0) printk("%s: interrupt received\n", __FUNCTION__);
+
+	for ( i = 0 ; i < MAX_ASIC_ISR_LOOPS && (GPLR0 & GPIO_H3900_ASIC2_INT) ; i++ ) {
+		u32 irq;
+		int j;
+		
+		/* KPIO */
+		irq = H3800_ASIC2_KPIINTFLAG;
+		if (0) printk("%s: KPIO 0x%08X\n", __FUNCTION__, irq );
+		for ( j = 0 ; j < H3800_KPIO_IRQ_COUNT ; j++ )
+			if ( irq & kpio_irq_mask[j] )
+				asm_do_IRQ( j + H3800_KPIO_IRQ_START, regs );
+
+		/* GPIO2 */
+		irq = H3800_ASIC2_GPIINTFLAG;
+		if (0) printk("%s: GPIO 0x%08X\n", __FUNCTION__, irq );
+		for ( j = 0 ; j < H3800_GPIO_IRQ_COUNT ; j++ )
+			if ( irq & gpio_irq_mask[j] )
+				asm_do_IRQ( j + H3800_GPIO_IRQ_START, regs );
+	}
+
+	if ( i >= MAX_ASIC_ISR_LOOPS )
+		printk("%s: interrupt processing overrun\n", __FUNCTION__);
+	
+	return IRQ_HANDLED;
+}
+
+static struct irqaction h3900_irq = {
+	name:     "h3900_asic",
+	handler:  h3900_IRQ_demux,
+	flags:    SA_INTERRUPT
+};
+
+u32 kpio_int_shadow = 0;
+
+/* mask_ack <- IRQ is first serviced.
+       mask <- IRQ is disabled.  
+     unmask <- IRQ is enabled 
+
+     The INTCLR registers are poorly documented.  I believe that writing
+     a "1" to the register clears the specific interrupt, but the documentation
+     indicates writing a "0" clears the interrupt.  In any case, they shouldn't
+     be read (that's the INTFLAG register)
+*/
+
+static void h3900_mask_ack_kpio_irq( unsigned int irq )
+{
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow &= ~mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+	H3800_ASIC2_KPIINTCLR  = mask;
+}
+
+static void h3900_mask_kpio_irq( unsigned int irq )
+{
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow &= ~mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+}
+
+static void h3900_unmask_kpio_irq( unsigned int irq )
+{
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow |= mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+}
+
+static void h3900_mask_ack_gpio_irq( unsigned int irq )
+{
+	u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START];
+	H3800_ASIC2_GPIINTSTAT &= ~mask;
+	H3800_ASIC2_GPIINTCLR   = mask;
+}
+
+static void h3900_mask_gpio_irq( unsigned int irq )
+{
+	u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START];
+	H3800_ASIC2_GPIINTSTAT &= ~mask;
+}
+
+static void h3900_unmask_gpio_irq( unsigned int irq )
+{
+	u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START];
+	H3800_ASIC2_GPIINTSTAT |= mask;
+}
+
+static struct irqchip h3900_kpio_irq_chip = {
+	.ack		= h3900_mask_ack_kpio_irq,
+	.mask		= h3900_mask_kpio_irq,
+	.unmask		= h3900_unmask_kpio_irq,
+};
+
+static struct irqchip h3900_gpio_irq_chip = {
+	.ack		= h3900_mask_ack_gpio_irq,
+	.mask		= h3900_mask_gpio_irq,
+	.unmask		= h3900_unmask_gpio_irq,
+};
+
+static void __init h3900_init_irq( void )
+{
+	int i;
+
+	/* Initialize standard IRQs */
+	pxa_init_irq();
+	
+	/* Disable all IRQs and set up clock */
+	H3800_ASIC2_KPIINTSTAT     =  0;     /* Disable all interrupts */
+	H3800_ASIC2_GPIINTSTAT     =  0;
+
+	H3800_ASIC2_KPIINTCLR      =  0;     /* Clear all KPIO interrupts */
+	H3800_ASIC2_GPIINTCLR      =  0;     /* Clear all GPIO interrupts */
+
+//	H3800_ASIC2_KPIINTCLR      =  0xffff;     /* Clear all KPIO interrupts */
+//	H3800_ASIC2_GPIINTCLR      =  0xffff;     /* Clear all GPIO interrupts */
+
+	H3800_ASIC2_CLOCK_Enable       |= ASIC2_CLOCK_EX0;   /* 32 kHZ crystal on */
+	H3800_ASIC2_INTR_ClockPrescale |= ASIC2_INTCPS_SET;
+	H3800_ASIC2_INTR_ClockPrescale  = ASIC2_INTCPS_CPS(0x0e) | ASIC2_INTCPS_SET;
+	H3800_ASIC2_INTR_TimerSet       = 1;
+
+	for ( i = 0 ; i < H3800_KPIO_IRQ_COUNT ; i++ ) {
+		int irq = i + H3800_KPIO_IRQ_START;
+		irq_desc[irq].valid    = 1;
+		irq_desc[irq].probe_ok = 1;
+		set_irq_chip(irq, &h3900_kpio_irq_chip);
+	}
+
+	for ( i = 0 ; i < H3800_GPIO_IRQ_COUNT ; i++ ) {
+		int irq = i + H3800_GPIO_IRQ_START;
+		irq_desc[irq].valid    = 1;
+		irq_desc[irq].probe_ok = 1;
+		set_irq_chip(irq, &h3900_gpio_irq_chip);
+	}
+
+	/* Don't start up the ADC IRQ automatically */
+	irq_desc[IRQ_H3800_ADC].noautoenable = 1;
+
+        /* note: set_irq_type takes a gpio number not a mask on pxa!! */
+	set_irq_type( GPIO_NR_H3900_ASIC2_INT, IRQT_RISING );
+	setup_irq( IRQ_GPIO_H3900_ASIC2_INT, &h3900_irq );
+}
+
+/*
+ * Common map_io initialization
+ */
+static short ipaq_gpio_modes[] = {
+	GPIO1_RTS_MD,
+	GPIO18_RDY_MD,
+	GPIO15_nCS_1_MD,
+	GPIO33_nCS_5_MD,
+	GPIO48_nPOE_MD,
+	GPIO49_nPWE_MD,
+	GPIO50_nPIOR_MD,
+	GPIO51_nPIOW_MD,
+	GPIO52_nPCE_1_MD,
+	GPIO53_nPCE_2_MD,
+	GPIO54_pSKTSEL_MD,
+	GPIO55_nPREG_MD,
+	GPIO56_nPWAIT_MD,
+	GPIO57_nIOIS16_MD,
+	GPIO78_nCS_2_MD,
+	GPIO79_nCS_3_MD,
+	GPIO80_nCS_4_MD,
+};
+
+static struct map_desc h3900_io_desc[] __initdata = {
+ /* virtual            physical           length      */
+  { H3600_BANK_2_VIRT, H3600_BANK_2_PHYS, 0x02800000, MT_DEVICE}, /* static memory bank 2  CS#2 */
+  { H3600_BANK_4_VIRT, H3600_BANK_4_PHYS, 0x00800000, MT_DEVICE}, /* static memory bank 4  CS#4 */
+  { H3600_BANK_5_VIRT, H3600_BANK_5_PHYS, 0x02000000, MT_DEVICE} /* static memory bank 4  CS#5 */
+};
+
+static void __init h3900_map_io(void)
+{
+	int i;
+
+	pxa_map_io();
+	iotable_init(h3900_io_desc, ARRAY_SIZE(h3900_io_desc));
+
+	/* Configure power management stuff. */
+	PWER = PWER_GPIO0 | PWER_RTC;
+	PFER = PWER_GPIO0 | PWER_RTC;
+	PRER = 0;
+	PCFR = PCFR_OPDE;
+	CKEN = CKEN6_FFUART;
+
+	PGSR0 = GPSRx_SleepValue;
+	PGSR1 = GPSRy_SleepValue;
+	PGSR2 = GPSRz_SleepValue;
+
+#if 0
+	/* redundant? */
+	for (i = 0; i < ARRAY_SIZE(ipaq_gpio_modes); i++) {
+		int mode = ipaq_gpio_modes[i];
+		if (0)
+			printk("ipaq gpio_mode: gpio_nr=%d dir=%d fn=%d\n",
+			       mode&GPIO_MD_MASK_NR, mode&GPIO_MD_MASK_DIR, mode&GPIO_MD_MASK_FN);
+		pxa_gpio_mode(mode);
+	}
+#endif
+
+	/* Set up GPIO direction and alternate function registers */
+	GAFR0_L = GAFR0x_InitValue;
+	GAFR0_U = GAFR1x_InitValue;
+	GAFR1_L = GAFR0y_InitValue;
+	GAFR1_U = GAFR1y_InitValue;
+	GAFR2_L = GAFR0z_InitValue;
+	GAFR2_U = GAFR1z_InitValue;
+	
+	GPDR0 = GPDRx_InitValue;
+	GPDR1 = GPDRy_InitValue;
+	GPDR2 = GPDRz_InitValue;
+
+	GPCR0 = 0x0fffffff;       /* All outputs are set low by default */
+
+	/* Add wakeup on AC plug/unplug */
+	PWER  |= PWER_GPIO8;
+	PFER  |= PWER_GPIO8;
+	PRER  |= PWER_GPIO8;
+
+	/* Select VLIO for ASIC3 */
+	MSC2 = (MSC2 & 0x0000ffff) | 0x74a40000; 
+
+	pxa_gpio_mode(GPIO33_nCS_5_MD);
+	
+	/* Set up ASIC #3 */
+	H3900_ASIC3_GPIO_A_DIR            = ASIC3GPIO_INIT_DIR;            /* All outputs */
+	H3900_ASIC3_GPIO_B_DIR            = ASIC3GPIO_INIT_DIR;            /* All outputs */
+	H3900_ASIC3_GPIO_C_DIR            = ASIC3GPIO_INIT_DIR;            /* All outputs */
+	H3900_ASIC3_GPIO_D_DIR            = ASIC3GPIO_INIT_DIR;            /* All outputs */
+
+	H3900_ASIC3_GPIO_B_MASK           = ASIC3GPIO_INIT_DIR;            /* No interrupts */
+	H3900_ASIC3_GPIO_B_SLEEP_MASK     = ASIC3GPIO_INIT_DIR;
+	H3900_ASIC3_GPIO_B_SLEEP_OUT      = ASIC3GPIO_SLEEP_OUT;
+	H3900_ASIC3_GPIO_B_BATT_FAULT_OUT = ASIC3GPIO_BATFALT_OUT;
+	H3900_ASIC3_GPIO_B_SLEEP_CONF     = 0;                             /* Disable auto sleep */
+	
+	H3900_ASIC3_GPIO_B_OUT = GPIO3_IR_ON_N | GPIO3_RS232_ON | GPIO3_TEST_POINT_123;
+	
+	/* Set up ASIC #2 */
+	H3800_ASIC2_GPIOPIOD    = GPIO2_IN_Y1_N | GPIO2_IN_X1_N;
+	H3800_ASIC2_GPOBFSTAT   = GPIO2_IN_Y1_N | GPIO2_IN_X1_N;
+
+	H3800_ASIC2_GPIODIR     = GPIO2_PEN_IRQ 
+		| GPIO2_SD_DETECT
+		| GPIO2_EAR_IN_N 
+		| GPIO2_USB_DETECT_N 
+		| GPIO2_SD_CON_SLT;
+	
+	ipaq_model_ops = h3900_model_ops;
+	
+	/* Turn off all LEDs */
+	ipaq_set_led (GREEN_LED, 0, 0);
+	ipaq_set_led (BLUE_LED, 0, 0);
+	ipaq_set_led (YELLOW_LED, 0, 0);
+}
+
+static int h3900_arch_init(void)
+{
+	int rc = 0;
+#ifdef CONFIG_LCD_MODULE
+	if (machine_is_h3900()) {
+		rc = lcd_module_register(&h3900_lcd_module);
+		if (rc)
+			return rc;
+		rc = backlight_module_register(&h3900_backlight_module);
+	}
+#endif
+	return rc;
+}
+
+device_initcall(h3900_arch_init);
+
+MACHINE_START(H3900, "HP iPAQ H3900")
+	MAINTAINER("HP Labs, Cambridge Research Labs")
+	BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
+	BOOT_PARAMS(0xa0000100)
+	MAPIO(h3900_map_io)
+	INITIRQ(h3900_init_irq)
+MACHINE_END
+
+
+
