[PATCH 2/5] OLPC: Fixup the DCON detction so it is less platform specific

Jordan Crouse jordan.crouse at amd.com
Wed Apr 25 20:31:27 EDT 2007


From: Jordan Crouse <jordan.crouse at amd.com>

The DCON now exists for different platforms - remove all GX references
move the DCON code to a more sane location, and fix the detection
mechanism.

Signed-off-by: Jordan Crouse <jordan.crouse at amd.com>
---

 .config                         |    4 
 arch/i386/kernel/olpc.c         |   20 +
 drivers/video/Kconfig           |    9 
 drivers/video/Makefile          |    1 
 drivers/video/geode/Kconfig     |    4 
 drivers/video/geode/Makefile    |    2 
 drivers/video/geode/gxfb_dcon.c |  690 -------------------------------------
 drivers/video/geode/gxfb_dcon.h |   75 ----
 drivers/video/olpc_dcon.c       |  718 +++++++++++++++++++++++++++++++++++++++
 drivers/video/olpc_dcon.h       |   75 ++++
 10 files changed, 820 insertions(+), 778 deletions(-)

diff --git a/.config b/.config
index 97f007f..c7e6863 100644
--- a/.config
+++ b/.config
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.21-rc7
-# Wed Apr 25 01:23:01 2007
+# Wed Apr 25 10:19:32 2007
 #
 CONFIG_X86_32=y
 CONFIG_GENERIC_TIME=y
@@ -1455,7 +1455,7 @@ CONFIG_FB_VESA=y
 CONFIG_FB_GEODE=y
 CONFIG_FB_GEODE_LX=y
 CONFIG_FB_GEODE_GX=y
-CONFIG_FB_GEODE_GX_DCON=y
+CONFIG_FB_OLPC_DCON=y
 # CONFIG_FB_GEODE_GX1 is not set
 # CONFIG_FB_VIRTUAL is not set
 
diff --git a/arch/i386/kernel/olpc.c b/arch/i386/kernel/olpc.c
index d79db20..9312f1c 100644
--- a/arch/i386/kernel/olpc.c
+++ b/arch/i386/kernel/olpc.c
@@ -242,18 +242,24 @@ static int __init olpc_init(void)
 
 	pm_power_off = olpc_power_off;
 
-	/* Read the DCON present bit in the CMOS and set the flag accordingly */
-	val = CMOS_READ(OLPC_CMOS_DCON_OFFSET);
+	/* Get the platform revision */
+	platform_detect(revision, sizeof(revision));
+
+	/* If olpc_dcon_present isn't set by the command line, then
+	 * "detect" it
+	 */
+
+	if (olpc_dcon_present == -1) {
+		/* B1 and greater always has a DCON */
 
-	if (olpc_dcon_present == -1)
-		olpc_dcon_present = (val & OLPC_CMOS_DCON_MASK);
+		if (olpc_platform_info.boardrev >= OLPC_REV_B1 &&
+			olpc_platform_info.boardrev != OLPC_REV_UNKNOWN)
+			olpc_dcon_present = 1;
+	}
 
 	if (olpc_dcon_present)
 		olpc_platform_info.flags |= OLPC_F_DCON;
 
-	/* Get the platform revision */
-	platform_detect(revision, sizeof(revision));
-
 	/* Get the EC revision */
 	ec_detect();
 
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index bebb916..417f941 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1633,6 +1633,15 @@ config FB_PS3_DEFAULT_SIZE_M
 	  The default value can be overridden on the kernel command line
 	  using the "ps3fb" option (e.g. "ps3fb=9M");
 
+config FB_OLPC_DCON
+	tristate "One Laptop Per Child Display CONtroller support"
+	depends on OLPC
+	select I2C
+	---help---
+	  Add support fo the OLPC DCON controller.  This controller is only
+	  available on OLPC platforms.   Unless you have one of these
+	  platforms,you will want to say 'N'.
+
 config FB_VIRTUAL
 	tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
 	depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 760305c..ee16f3b 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB)  += pnx4008/
 obj-$(CONFIG_FB_IBM_GXT4500)	  += gxt4500.o
 obj-$(CONFIG_FB_PS3)		  += ps3fb.o
 obj-$(CONFIG_FB_SM501)            += sm501fb.o
+obj-$(CONFIG_FB_OLPC_DCON)	  += olpc_dcon.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_VESA)             += vesafb.o
diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig
index a7a9734..1df7702 100644
--- a/drivers/video/geode/Kconfig
+++ b/drivers/video/geode/Kconfig
@@ -30,9 +30,9 @@ config FB_GEODE_GX
 
 	  If unsure, say N.
 
-config FB_GEODE_GX_DCON
+config FB_OLPC_DCON
 	tristate "One Laptop Per Child Display CONtroller support"
-	depends on FB_GEODE_GX
+	depends on OLPC
 	select I2C
 	---help---
 	  Add support fo the OLPC DCON controller.  This controller is only
diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile
index ea84e17..957304b 100644
--- a/drivers/video/geode/Makefile
+++ b/drivers/video/geode/Makefile
@@ -4,8 +4,6 @@ obj-$(CONFIG_FB_GEODE_GX1) += gx1fb.o
 obj-$(CONFIG_FB_GEODE_GX)  += gxfb.o
 obj-$(CONFIG_FB_GEODE_LX)  += lxfb.o
 
-obj-$(CONFIG_FB_GEODE_GX_DCON) += gxfb_dcon.o
-
 gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o
 gxfb-objs  := gxfb_core.o display_gx.o video_gx.o
 lxfb-objs  := lxfb_core.o lxfb_ops.o
diff --git a/drivers/video/geode/gxfb_dcon.c b/drivers/video/geode/gxfb_dcon.c
deleted file mode 100644
index 2c9d148..0000000
--- a/drivers/video/geode/gxfb_dcon.c
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
- *
- * Jordan's work is copyright (C) 2006 Advanced Micro Devices, Inc.
- *
- * 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 1 or
- * (at your option) any later version.
- */
-
-
-#include <linux/kernel.h>
-#include <linux/fb.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/i2c-id.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/backlight.h>
-#include <linux/device.h>
-#include <asm/uaccess.h>
-#include <linux/ctype.h>
-#include <linux/reboot.h>
-#include <asm/tsc.h>
-
-#include "geodefb.h"
-#include "gxfb_dcon.h"
-
-/* Module definitions */
-
-static int resumeline = 905;
-module_param(resumeline, int, 0444);
-
-static int noinit;
-module_param(noinit, int, 0444);
-
-/* Default off since it doesn't work on DCON ASIC in B-test OLPC board */
-static int useaa = 1;
-module_param(useaa, int, 0444);
-
-/* I2C structures */
-
-static struct i2c_driver dcon_driver;
-static struct i2c_client *dcon_client;
-
-/* Platform devices */
-static struct platform_device *dcon_device;
-
-/* Backlight device */
-static struct backlight_device *dcon_bl_dev;
-
-/* Base address of the GPIO registers */
-static unsigned long gpio_base;
-
-/* fbinfo from the GX framebuffer driver */
-static struct fb_info *fbinfo;
-
-/* Current source */
-static int dcon_source = DCON_SOURCE_CPU;
-
-/* Current output type */
-static int dcon_output = DCON_OUTPUT_COLOR;
-
-/* Current sleep status (not yet implemented) */
-static int dcon_sleep_val = DCON_ACTIVE;
-
-/* Shadow register for the DCON_REG_MODE register */
-static unsigned short dcon_disp_mode;
-
-/* Variables used during switches */
-static int dcon_switched;
-
-static DECLARE_WAIT_QUEUE_HEAD(dcon_wait_queue);
-
-extern int gxfb_powerdown(struct fb_info *info);
-extern int gxfb_pre_powerup(struct fb_info *info);
-extern int gxfb_powerup(struct fb_info *info);
-
-static unsigned short normal_i2c[] = { 0x0D, I2C_CLIENT_END };
-I2C_CLIENT_INSMOD;
-
-#define dcon_write(reg,val) i2c_smbus_write_word_data(dcon_client,reg,val)
-#define dcon_read(reg) i2c_smbus_read_word_data(dcon_client,reg)
-
-/* The current backlight value - this saves us some smbus traffic */
-static int gxfb_bl_val = -1;
-
-/* ===== API functions - these are called by a variety of users ==== */
-
-/* Backlight notes - turning off the backlight enable bit in the DCON
- * doesn't save us any power over just pushing the BL to zero, so we
- * don't use that bit in this code.
- */
-
-static int dcon_get_backlight(void)
-{
-	if (dcon_client == NULL)
-		return 0;
-
-	if (gxfb_bl_val == -1)
-		gxfb_bl_val = dcon_read(DCON_REG_BRIGHT) & 0x0F;
-
-	return gxfb_bl_val;
-}
-
-static void dcon_set_backlight(int level)
-{
-	if (dcon_client == NULL)
-		return;
-
-	if (gxfb_bl_val == (level & 0x0F))
-		return;
-
-	gxfb_bl_val = level & 0x0F;
-	dcon_write(DCON_REG_BRIGHT, gxfb_bl_val);
-}
-
-/* Set the output type to either color or mono */
-
-static int dcon_set_output(int arg)
-{
-	if (dcon_output == arg)
-		return 0;
-
-	dcon_output = arg;
-
-	if (arg == DCON_OUTPUT_MONO) {
-		dcon_disp_mode &= ~(MODE_CSWIZZLE | MODE_COL_AA);
-		dcon_disp_mode |= MODE_MONO_LUMA;
-	}
-	else {
-		dcon_disp_mode &= ~(MODE_MONO_LUMA);
-		dcon_disp_mode |= MODE_CSWIZZLE;
-		if (useaa)
-			dcon_disp_mode |= MODE_COL_AA;
-	}
-
-	dcon_write(DCON_REG_MODE, dcon_disp_mode);
-	return 0;
-}
-
-/* For now, this will be really stupid - we need to address how
- * DCONLOAD works in a sleep and account for it accordingly
- */
-
-static void dcon_sleep(int state)
-{
-	/* Turn off the backlight and put the DCON to sleep */
-
-	if (state == dcon_sleep_val)
-		return;
-
-	if (state == DCON_SLEEP) {
-		dcon_disp_mode &= ~MODE_BL_ENABLE;
-		dcon_disp_mode |= MODE_SLEEP;
-	}
-	else {
-		dcon_disp_mode |= MODE_BL_ENABLE;
-		dcon_disp_mode &= ~MODE_SLEEP;
-	}
-
-	dcon_sleep_val = state;
-	dcon_write(DCON_REG_MODE, dcon_disp_mode);
-
-	/* We should turn off some stuff in the framebuffer - but what? */
-}
-
-/* Set the source of the display (CPU or DCON) */
-
-static int dcon_set_source(int arg)
-{
-	DECLARE_WAITQUEUE(wait, current);
-
-	if (dcon_source == arg)
-		return 0;
-
-	dcon_switched = 0;
-
-	switch (arg) {
-	case DCON_SOURCE_CPU:
-
-		/* FIXME:  Set the task uninterruptable for this bit? */
-		/* Prepare to turn on the graphics engine */
-		gxfb_pre_powerup(fbinfo);
-
-		/* Enable the scanline interrupt bit */
-		dcon_write(DCON_REG_MODE, dcon_disp_mode | MODE_SCAN_INT);
-
-		/* Wait up to one second for the mode to change */
-		wait_event_timeout(dcon_wait_queue, dcon_switched == 1, HZ);
-
-		/* Turn off the scanline interrupt */
-		dcon_write(DCON_REG_MODE, dcon_disp_mode);
-
-		if (!dcon_switched) {
-			printk(KERN_ERR "dcon:  Timeout entering CPU mode\n");
-			return -1;
-		}
-
-		/* Turn on the graphics engine */
-		gxfb_powerup(fbinfo);
-		outl(1<<11, gpio_base + GPIOx_OUT_VAL);
-
-		printk(KERN_INFO "dcon: The CPU has control\n");
-		break;
-	case DCON_SOURCE_DCON:
-	{
-		int t;
-
-		add_wait_queue(&dcon_wait_queue, &wait);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-
-		/* Clear GPIO11 (DCONLOAD) - this implies that the DCON is in
-		   control */
-
-		outl(1 << (11 + 16), gpio_base + GPIOx_OUT_VAL);
-
-		t = schedule_timeout(HZ/2);
-		remove_wait_queue(&dcon_wait_queue, &wait);
-		set_current_state(TASK_RUNNING);
-
-		if (!dcon_switched) {
-			printk(KERN_ERR "dcon: Timeout entering DCON mode\n");
-
-			/* Set DCONLOAD back to CPU control */
-			outl(1 << 11, gpio_base + GPIOx_OUT_VAL);
-			return -1;
-		}
-
-		/* Turn off the graphics engine completely */
-		gxfb_powerdown(fbinfo);
-		printk(KERN_INFO "dcon: The DCON has control\n");
-		break;
-	}
-	default:
-		return -EINVAL;
-	}
-
-	dcon_source = arg;
-	return 0;
-}
-
-static int dconbl_set(struct backlight_device *dev) {
-
-	int level = dev->props.brightness;
-
-	if (dev->props.power != FB_BLANK_UNBLANK)
-		level = 0;
-
-	dcon_set_backlight(level);
-	return 0;
-}
-
-static int dconbl_get(struct backlight_device *dev) {
-	return dcon_get_backlight();
-}
-
-static ssize_t dcon_mode_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%4.4X\n", dcon_disp_mode);
-}
-
-static ssize_t dcon_sleep_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%d\n", dcon_sleep_val);
-}
-
-static ssize_t dcon_source_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%d\n", dcon_source);
-}
-
-static ssize_t dcon_output_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%d\n", dcon_output);
-}
-
-static int _strtoul(const char *buf, int len, unsigned int *val)
-{
-
-	char *endp;
-	unsigned int output = simple_strtoul(buf, &endp, 0);
-	int size = endp - buf;
-
-	if (*endp && isspace(*endp))
-		size++;
-
-	if (size != len)
-		return -EINVAL;
-
-	*val = output;
-	return 0;
-}
-
-static ssize_t dcon_output_store(struct device *dev,
-	struct device_attribute *attr, const char *buf, size_t count)
-{
-	int output;
-	int rc = -EINVAL;
-
-	if (_strtoul(buf, count, &output))
-		return -EINVAL;
-
-	if (output == DCON_OUTPUT_COLOR || output == DCON_OUTPUT_MONO) {
-		dcon_set_output(output);
-		rc = count;
-	}
-
-	return rc;
-}
-
-static ssize_t dcon_source_store(struct device *dev,
-	struct device_attribute *attr, const char *buf, size_t count)
-{
-	int output;
-	int rc = -EINVAL;
-
-	if (_strtoul(buf, count, &output))
-		return -EINVAL;
-
-	dcon_set_source(output);
-	rc = count;
-
-	return rc;
-}
-
-static ssize_t dcon_sleep_store(struct device *dev,
-	struct device_attribute *attr, const char *buf, size_t count)
-{
-	int output;
-
-	if (_strtoul(buf, count, &output))
-		return -EINVAL;
-
-	dcon_sleep(output ? DCON_SLEEP : DCON_ACTIVE);
-	return count;
-}
-
-static struct device_attribute dcon_device_files[] = {
-	__ATTR(mode, 0444, dcon_mode_show, NULL),
-	__ATTR(sleep, 0644, dcon_sleep_show, dcon_sleep_store),
-	__ATTR(source, 0644, dcon_source_show, dcon_source_store),
-	__ATTR(output, 0644, dcon_output_show, dcon_output_store),
-};
-
-static struct backlight_ops dcon_bl_ops = {
-	.get_brightness = dconbl_get,
-	.update_status = dconbl_set
-};
-
-/* List of GPIOs that we care about:
-   (in)  GPIO12   -- DCONBLNK
-   (in)  GPIO[56] -- DCONSTAT[01]
-   (out) GPIO11   -- DCONLOAD
-*/
-
-#define IN_GPIOS ((1<<5) | (1<<6) | (1<<7) | (1<<12))
-#define OUT_GPIOS (1<<11)
-
-static irqreturn_t dcon_interrupt(int, void *);
-
-static int dcon_request_irq(void)
-{
-	unsigned long lo, hi;
-	unsigned char lob;
-
-	rdmsr(MSR_LBAR_GPIO, lo, hi);
-
-	/* Check the mask and whether GPIO is enabled (sanity check) */
-	if (hi != 0x0000f001) {
-		printk(KERN_ERR "GPIO not enabled -- cannot use DCON\n");
-		return -ENODEV;
-	}
-
-	/* Mask off the IO base address */
-	gpio_base = lo & 0x0000ff00;
-
-	/* Turn off the event enable for GPIO7 just to be safe */
-	outl(1 << (16+7), gpio_base + GPIOx_EVNT_EN);
-
-	/* Set the directions for the GPIO pins */
-	outl(OUT_GPIOS | (IN_GPIOS << 16), gpio_base + GPIOx_OUT_EN);
-	outl(IN_GPIOS | (OUT_GPIOS << 16), gpio_base + GPIOx_IN_EN);
-
-	/* Set up the interrupt mappings first, so we can collect the
-	 * first interrupt when it happens
-	 */
-
-	hi = inl(gpio_base + GPIO_MAP_X);
-	hi &= 0x0fffffff;
-	hi |= 0x70000000;
-	outl(hi, gpio_base + GPIO_MAP_X);
-
-	/* Don't map the GPIO12 interrupt */
-
-	hi = inl(gpio_base + GPIO_MAP_Y);
-	hi &= 0xfff0ffff;
-	hi |= 0x00000000;
-	outl(hi, gpio_base + GPIO_MAP_Y);
-
-	/* Enable GPIO IRQ 7 to trigger the PIC interrupt in the Z sources */
-
-	rdmsr(0x51400023, hi, lo);
-	hi &= 0x0fffffff;
-	hi |= (DCON_IRQ << 28);
-	wrmsrl(0x51400023, hi);
-
-	/* Select edge level for interrupt (in PIC) */
-
-	lob = inb(0x4d0);
-	lob &= ~(1 << DCON_IRQ);
-	outb(lob, 0x4d0);
-
-	/* Register the interupt handler */
-	if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver))
-		return -EIO;
-
-	/* Clear INV_EN for GPIO7 (DCONIRQ) */
-	outl((1<<(16+7)), gpio_base + GPIOx_INV_EN);
-
-	/* Enable filter for GPIO12 (DCONBLANK) */
-	outl(1<<(12), gpio_base + GPIOx_IN_FLTR_EN);
-
-	/* Disable filter for GPIO7 */
-	outl(1<<(16+7), gpio_base + GPIOx_IN_FLTR_EN);
-
-	/* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
-
-	outl(1<<(16+7), gpio_base + GPIOx_EVNTCNT_EN);
-	outl(1<<(16+12), gpio_base + GPIOx_EVNTCNT_EN);
-
-	/* Add GPIO12 to the Filter Event Pair #7 */
-	outb(12, gpio_base + GPIO_FE7_SEL);
-
-	/* Turn off negative Edge Enable for GPIO12 */
-	outl(1<<(16+12), gpio_base + GPIOx_NEGEDGE_EN);
-
-	/* Enable negative Edge Enable for GPIO7 */
-	outl(1<<7, gpio_base + GPIOx_NEGEDGE_EN);
-
-	/* Zero the filter amount for Filter Event Pair #7 */
-	outw(0, gpio_base + GPIO_FLT7_AMNT);
-
-	/* Clear the negative edge status for GPIO7 and GPIO12 */
-	outl((1<<7) | (1<<12), gpio_base+0x4c);
-
-	/* FIXME:  Clear the posiitive status as well, just to be sure */
-	outl((1<<7) | (1<<12), gpio_base+0x48);
-
-	/* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
-	outl((1<<(7))|(1<<12), gpio_base + GPIOx_EVNT_EN);
-
-	/* Assert DCONLOAD - this asserts that the CPU is still in control */
-	outl(1<<11, gpio_base + GPIOx_OUT_VAL);
-	return 0;
-}
-
-static int dcon_probe(struct i2c_adapter *adap, int addr, int kind)
-{
-	struct i2c_client *client;
-	struct pci_dev *dev;
-	uint16_t ver;
-	int rc, i;
-
-	dev = pci_get_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_GX_VIDEO, NULL);
-	if (!dev || (fbinfo = pci_get_drvdata(dev)) == NULL) {
-		printk(KERN_ERR "dcon:  Couldn't find the GX GPU device\n");
-		return -ENXIO;
-	}
-
-	if (adap->id != I2C_HW_SMBUS_SCX200) {
-		printk(KERN_ERR "gxfb-dcon: Invalid I2C bus (%d not %d)\n",
-		       adap->id, I2C_HW_SMBUS_SCX200);
-		return -ENXIO;
-	}
-
-	client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-	if (client == NULL)
-		return -ENOMEM;
-
-	strncpy(client->name, "DCON", I2C_NAME_SIZE);
-	client->addr = addr;
-	client->adapter = adap;
-	client->driver = &dcon_driver;
-
-	if ((rc = i2c_attach_client(client)) != 0) {
-		printk(KERN_ERR "gxfb-dcon: Unable to attach the I2C client.\n");
-		goto eclient;
-	}
-
-	ver = i2c_smbus_read_word_data(client, DCON_REG_ID);
-
-	if ((ver >> 8) != 0xDC) {
-		printk(KERN_ERR "gxfb-dcon: DCON ID not 0xDCxx: 0x%04x instead.\n", ver);
-		rc = -ENXIO;
-		goto ei2c;
-	}
-
-	if ((rc = dcon_request_irq())) {
-		printk(KERN_ERR "gxfb-dcon: Unable to grab IRQ.\n");
-		goto ei2c;
-	}
-
-	if (ver < 0xdc02 && !noinit) {
-		/* Initialize the DCON registers */
-
-		/* Start with work-arounds for DCON ASIC */
-		i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
-		i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
-		i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
-		i2c_smbus_write_word_data(client, 0x0b, 0x007a);
-		i2c_smbus_write_word_data(client, 0x36, 0x025c);
-		i2c_smbus_write_word_data(client, 0x37, 0x025e);
-		
-		/* Initialise SDRAM */
-
-		i2c_smbus_write_word_data(client, 0x3b, 0x002b);
-		i2c_smbus_write_word_data(client, 0x41, 0x0101);
-		i2c_smbus_write_word_data(client, 0x42, 0x0101);
-	}
-
-	/* Colour swizzle, AA, no passthrough, backlight */
-
-	dcon_disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE | MODE_CSWIZZLE;
-	if (useaa)
-		dcon_disp_mode |= MODE_COL_AA;
-	i2c_smbus_write_word_data(client, DCON_REG_MODE, dcon_disp_mode);
-
-
-	/* Set the scanline to interrupt on during resume */
-
-	i2c_smbus_write_word_data(client, DCON_REG_SCAN_INT, resumeline);
-
-	/* Add the DCON device */
-
-	dcon_device = platform_device_alloc("dcon", -1);
-
-	if (dcon_device == NULL) {
-		printk(KERN_ERR "dcon:  Unable to create the DCON device\n");
-		rc = -ENOMEM;
-		goto eirq;
-	}
-
-	if ((rc = platform_device_add(dcon_device))) {
-		printk(KERN_ERR "dcon:  Unable to add the DCON device\n");
-		goto edev;
-	}
-
-	for(i = 0; i < ARRAY_SIZE(dcon_device_files); i++)
-		device_create_file(&dcon_device->dev, &dcon_device_files[i]);
-
-	/* Add the backlight device for the DCON */
-
-	dcon_client = client;
-
-	dcon_bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev,
-		NULL, &dcon_bl_ops);
-
-	if (IS_ERR(dcon_bl_dev)) {
-		printk(KERN_INFO "Could not register the backlight device for the DCON (%ld)\n", PTR_ERR(dcon_bl_dev));
-		dcon_bl_dev = NULL;
-	}
-	else {
-		dcon_bl_dev->props.max_brightness = 15;
-		dcon_bl_dev->props.power = FB_BLANK_UNBLANK;
-		dcon_bl_dev->props.brightness = dcon_get_backlight();
-
-		backlight_update_status(dcon_bl_dev);
-	}
-
-	printk(KERN_INFO "gxfb-dcon: Discovered DCON version %x\n", ver & 0xFF);
-
-	return 0;
-
- edev:
-	platform_device_unregister(dcon_device);
-	dcon_device = NULL;
- eirq:
-	free_irq(DCON_IRQ, &dcon_driver);
- ei2c:
-	i2c_detach_client(client);
- eclient:
-	kfree(client);
-
-	return rc;
-}
-
-static int dcon_attach(struct i2c_adapter *adap)
-{
-	int ret;
-
-	ret = i2c_probe(adap, &addr_data, dcon_probe);
-
-	if (dcon_client == NULL)
-		printk(KERN_ERR "gxfb-dcon: No DCON found on SMBus\n");
-
-	return ret;
-}
-
-static int dcon_detach(struct i2c_client *client)
-{
-	int rc;
-	dcon_client = NULL;
-
-	free_irq(DCON_IRQ, &dcon_driver);
-
-	if ((rc = i2c_detach_client(client)) == 0)
-		kfree(i2c_get_clientdata(client));
-
-	if (dcon_bl_dev != NULL)
-		backlight_device_unregister(dcon_bl_dev);
-
-	if (dcon_device != NULL)
-		platform_device_unregister(dcon_device);
-
-	return rc;
-}
-
-static irqreturn_t dcon_interrupt(int irq, void *id)
-{
-	int status = inl(gpio_base + GPIOx_READ_BACK) >> 5;
-
-	/* Clear the negative edge status for GPIO7 */
-	outl(1 << 7, gpio_base + GPIOx_NEGEDGE_STS);
-
-	switch (status & 3) {
-	case 3:
-		printk(KERN_DEBUG "gxfb-dcon: DCONLOAD_MISSED interrupt\n");
-		break;
-	case 2:	/* switch to DCON mode */
-	case 1: /* switch to CPU mode */
-		dcon_switched = 1;
-		wake_up(&dcon_wait_queue);
-		break;
-	case 0:
-		printk(KERN_DEBUG "gxfb-dcon: scanline interrupt w/CPU\n");
-	}
-
-	return IRQ_HANDLED;
-}
-
-static int dcon_reboot_notify(struct notifier_block *nb, unsigned long foo, void *bar)
-{
-	if (dcon_client == NULL)
-		return 0;
-
-	/* Turn off the DCON. Entirely. */
-	dcon_write(DCON_REG_MODE, 0x39);
-	dcon_write(DCON_REG_MODE, 0x32);
-	return 0;
-}
-
-static struct i2c_driver dcon_driver = {
-	.driver = {
-		.name	= "DCON",
-	},
-	.id = I2C_DRIVERID_DCON,
-	.attach_adapter = dcon_attach,
-	.detach_client = dcon_detach,
-};
-
-static struct notifier_block dcon_nb = {
-	.notifier_call = dcon_reboot_notify,
-	.priority = -1,
-};
-
-static int __init gxfb_dcon_init(void)
-{
-	i2c_add_driver(&dcon_driver);
-	register_reboot_notifier(&dcon_nb);
-	return 0;
-}
-
-static void __exit gxfb_dcon_exit(void)
-{
-	unregister_reboot_notifier(&dcon_nb);
-	i2c_del_driver(&dcon_driver);
-}
-
-module_init(gxfb_dcon_init);
-module_exit(gxfb_dcon_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/geode/gxfb_dcon.h b/drivers/video/geode/gxfb_dcon.h
deleted file mode 100644
index e758d79..0000000
--- a/drivers/video/geode/gxfb_dcon.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef GXFB_DCON_H_
-#define GXFB_DCON_H_
-
-/* DCON registers */
-
-#define DCON_REG_ID		 0
-#define DCON_REG_MODE		 1
-
-#define MODE_PASSTHRU	(1<<0)
-#define MODE_SLEEP	(1<<1)
-#define MODE_SLEEP_AUTO	(1<<2)
-#define MODE_BL_ENABLE	(1<<3)
-#define MODE_BLANK	(1<<4)
-#define MODE_CSWIZZLE	(1<<5)
-#define MODE_COL_AA	(1<<6)
-#define MODE_MONO_LUMA	(1<<7)
-#define MODE_SCAN_INT	(1<<8)
-#define MODE_CLOCKDIV	(1<<9)
-#define MODE_DEBUG	(1<<14)
-#define MODE_SELFTEST	(1<<15)
-
-#define DCON_REG_HRES		2
-#define DCON_REG_HTOTAL		3
-#define DCON_REG_HSYNC_WIDTH	4
-#define DCON_REG_VRES		5
-#define DCON_REG_VTOTAL		6
-#define DCON_REG_VSYNC_WIDTH	7
-#define DCON_REG_TIMEOUT	8
-#define DCON_REG_SCAN_INT	9
-#define DCON_REG_BRIGHT		10
-
-/* GPIO registers (CS5536) */
-
-#define MSR_LBAR_GPIO		0x5140000C
-
-#define GPIOx_OUT_VAL     0x00
-#define GPIOx_OUT_EN      0x04
-#define GPIOx_IN_EN       0x20
-#define GPIOx_INV_EN      0x24
-#define GPIOx_IN_FLTR_EN  0x28
-#define GPIOx_EVNTCNT_EN  0x2C
-#define GPIOx_READ_BACK   0x30
-#define GPIOx_EVNT_EN     0x38
-#define GPIOx_NEGEDGE_EN  0x44
-#define GPIOx_NEGEDGE_STS 0x4C
-#define GPIO_FLT7_AMNT    0xD8
-#define GPIO_MAP_X        0xE0
-#define GPIO_MAP_Y        0xE4
-#define GPIO_FE7_SEL      0xF7
-
-
-/* Status values */
-
-#define DCONSTAT_SCANINT	0
-#define DCONSTAT_SCANINT_DCON	1
-#define DCONSTAT_DISPLAYLOAD	2
-#define DCONSTAT_MISSED		3
-
-/* Source values */
-
-#define DCON_SOURCE_DCON        0
-#define DCON_SOURCE_CPU         1
-
-/* Output values */
-#define DCON_OUTPUT_COLOR       0
-#define DCON_OUTPUT_MONO        1
-
-/* Sleep values */
-#define DCON_ACTIVE             0
-#define DCON_SLEEP              1
-
-/* Interrupt */
-#define DCON_IRQ                6
-
-#endif
diff --git a/drivers/video/olpc_dcon.c b/drivers/video/olpc_dcon.c
new file mode 100644
index 0000000..778c24b
--- /dev/null
+++ b/drivers/video/olpc_dcon.c
@@ -0,0 +1,718 @@
+/*
+ * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
+ *
+ * Jordan's work is copyright (C) 2006 Advanced Micro Devices, Inc.
+ *
+ * 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 1 or
+ * (at your option) any later version.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/i2c-id.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/backlight.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/reboot.h>
+#include <asm/tsc.h>
+#include <asm/olpc.h>
+
+#include "olpc_dcon.h"
+
+/* Module definitions */
+
+static int resumeline = 905;
+module_param(resumeline, int, 0444);
+
+static int noinit;
+module_param(noinit, int, 0444);
+
+/* Default off since it doesn't work on DCON ASIC in B-test OLPC board */
+static int useaa = 1;
+module_param(useaa, int, 0444);
+
+/* I2C structures */
+
+static struct i2c_driver dcon_driver;
+static struct i2c_client *dcon_client;
+
+/* Platform devices */
+static struct platform_device *dcon_device;
+
+/* Backlight device */
+static struct backlight_device *dcon_bl_dev;
+
+/* Base address of the GPIO registers */
+static unsigned long gpio_base;
+
+/* fbinfo from the GX framebuffer driver */
+static struct fb_info *fbinfo;
+
+/* Current source */
+static int dcon_source = DCON_SOURCE_CPU;
+
+/* Current output type */
+static int dcon_output = DCON_OUTPUT_COLOR;
+
+/* Current sleep status (not yet implemented) */
+static int dcon_sleep_val = DCON_ACTIVE;
+
+/* Shadow register for the DCON_REG_MODE register */
+static unsigned short dcon_disp_mode;
+
+/* Variables used during switches */
+static int dcon_switched;
+
+static DECLARE_WAIT_QUEUE_HEAD(dcon_wait_queue);
+
+#ifdef CONFIG_FB_GEODE_GX
+extern int gxfb_powerdown(struct fb_info *info);
+extern int gxfb_pre_powerup(struct fb_info *info);
+extern int gxfb_powerup(struct fb_info *info);
+#endif
+
+static unsigned short normal_i2c[] = { 0x0D, I2C_CLIENT_END };
+I2C_CLIENT_INSMOD;
+
+#define dcon_write(reg,val) i2c_smbus_write_word_data(dcon_client,reg,val)
+#define dcon_read(reg) i2c_smbus_read_word_data(dcon_client,reg)
+
+/* The current backlight value - this saves us some smbus traffic */
+static int bl_val = -1;
+
+/* ===== API functions - these are called by a variety of users ==== */
+
+/* Backlight notes - turning off the backlight enable bit in the DCON
+ * doesn't save us any power over just pushing the BL to zero, so we
+ * don't use that bit in this code.
+ */
+
+static int dcon_get_backlight(void)
+{
+	if (dcon_client == NULL)
+		return 0;
+
+	if (bl_val == -1)
+		bl_val = dcon_read(DCON_REG_BRIGHT) & 0x0F;
+
+	return bl_val;
+}
+
+static void dcon_set_backlight(int level)
+{
+	if (dcon_client == NULL)
+		return;
+
+	if (bl_val == (level & 0x0F))
+		return;
+
+	bl_val = level & 0x0F;
+	dcon_write(DCON_REG_BRIGHT, bl_val);
+}
+
+/* Set the output type to either color or mono */
+
+static int dcon_set_output(int arg)
+{
+	if (dcon_output == arg)
+		return 0;
+
+	dcon_output = arg;
+
+	if (arg == DCON_OUTPUT_MONO) {
+		dcon_disp_mode &= ~(MODE_CSWIZZLE | MODE_COL_AA);
+		dcon_disp_mode |= MODE_MONO_LUMA;
+	}
+	else {
+		dcon_disp_mode &= ~(MODE_MONO_LUMA);
+		dcon_disp_mode |= MODE_CSWIZZLE;
+		if (useaa)
+			dcon_disp_mode |= MODE_COL_AA;
+	}
+
+	dcon_write(DCON_REG_MODE, dcon_disp_mode);
+	return 0;
+}
+
+/* For now, this will be really stupid - we need to address how
+ * DCONLOAD works in a sleep and account for it accordingly
+ */
+
+static void dcon_sleep(int state)
+{
+	/* Turn off the backlight and put the DCON to sleep */
+
+	if (state == dcon_sleep_val)
+		return;
+
+	if (state == DCON_SLEEP) {
+		dcon_disp_mode &= ~MODE_BL_ENABLE;
+		dcon_disp_mode |= MODE_SLEEP;
+	}
+	else {
+		dcon_disp_mode |= MODE_BL_ENABLE;
+		dcon_disp_mode &= ~MODE_SLEEP;
+	}
+
+	dcon_sleep_val = state;
+	dcon_write(DCON_REG_MODE, dcon_disp_mode);
+
+	/* We should turn off some stuff in the framebuffer - but what? */
+}
+
+/* Set the source of the display (CPU or DCON) */
+
+static int dcon_set_source(int arg)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (dcon_source == arg)
+		return 0;
+
+	dcon_switched = 0;
+
+	switch (arg) {
+	case DCON_SOURCE_CPU:
+
+		/* FIXME:  Set the task uninterruptable for this bit? */
+		/* Prepare to turn on the graphics engine */
+
+#ifdef CONFIG_FB_GEODE_GX
+		gxfb_pre_powerup(fbinfo);
+#endif
+
+		/* Enable the scanline interrupt bit */
+		dcon_write(DCON_REG_MODE, dcon_disp_mode | MODE_SCAN_INT);
+
+		/* Wait up to one second for the mode to change */
+		wait_event_timeout(dcon_wait_queue, dcon_switched == 1, HZ);
+
+		/* Turn off the scanline interrupt */
+		dcon_write(DCON_REG_MODE, dcon_disp_mode);
+
+		if (!dcon_switched) {
+			printk(KERN_ERR "olpc-dcon:  Timeout entering CPU mode\n");
+			return -1;
+		}
+
+		/* Turn on the graphics engine */
+#ifdef CONFIG_FB_GEODE_GX
+		gxfb_powerup(fbinfo);
+#endif
+		outl(1<<11, gpio_base + GPIOx_OUT_VAL);
+
+		printk(KERN_INFO "olpc-dcon: The CPU has control\n");
+		break;
+	case DCON_SOURCE_DCON:
+	{
+		int t;
+
+		add_wait_queue(&dcon_wait_queue, &wait);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+
+		/* Clear GPIO11 (DCONLOAD) - this implies that the DCON is in
+		   control */
+
+		outl(1 << (11 + 16), gpio_base + GPIOx_OUT_VAL);
+
+		t = schedule_timeout(HZ/2);
+		remove_wait_queue(&dcon_wait_queue, &wait);
+		set_current_state(TASK_RUNNING);
+
+		if (!dcon_switched) {
+			printk(KERN_ERR "olpc-dcon: Timeout entering DCON mode\n");
+
+			/* Set DCONLOAD back to CPU control */
+			outl(1 << 11, gpio_base + GPIOx_OUT_VAL);
+			return -1;
+		}
+
+		/* Turn off the graphics engine completely */
+#ifdef CONFIG_FB_GEODE_GX
+		gxfb_powerdown(fbinfo);
+#endif
+		printk(KERN_INFO "olpc-dcon: The DCON has control\n");
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	dcon_source = arg;
+	return 0;
+}
+
+static int dconbl_set(struct backlight_device *dev) {
+
+	int level = dev->props.brightness;
+
+	if (dev->props.power != FB_BLANK_UNBLANK)
+		level = 0;
+
+	dcon_set_backlight(level);
+	return 0;
+}
+
+static int dconbl_get(struct backlight_device *dev) {
+	return dcon_get_backlight();
+}
+
+static ssize_t dcon_mode_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%4.4X\n", dcon_disp_mode);
+}
+
+static ssize_t dcon_sleep_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", dcon_sleep_val);
+}
+
+static ssize_t dcon_source_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", dcon_source);
+}
+
+static ssize_t dcon_output_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", dcon_output);
+}
+
+static int _strtoul(const char *buf, int len, unsigned int *val)
+{
+
+	char *endp;
+	unsigned int output = simple_strtoul(buf, &endp, 0);
+	int size = endp - buf;
+
+	if (*endp && isspace(*endp))
+		size++;
+
+	if (size != len)
+		return -EINVAL;
+
+	*val = output;
+	return 0;
+}
+
+static ssize_t dcon_output_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int output;
+	int rc = -EINVAL;
+
+	if (_strtoul(buf, count, &output))
+		return -EINVAL;
+
+	if (output == DCON_OUTPUT_COLOR || output == DCON_OUTPUT_MONO) {
+		dcon_set_output(output);
+		rc = count;
+	}
+
+	return rc;
+}
+
+static ssize_t dcon_source_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int output;
+	int rc = -EINVAL;
+
+	if (_strtoul(buf, count, &output))
+		return -EINVAL;
+
+	dcon_set_source(output);
+	rc = count;
+
+	return rc;
+}
+
+static ssize_t dcon_sleep_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int output;
+
+	if (_strtoul(buf, count, &output))
+		return -EINVAL;
+
+	dcon_sleep(output ? DCON_SLEEP : DCON_ACTIVE);
+	return count;
+}
+
+static struct device_attribute dcon_device_files[] = {
+	__ATTR(mode, 0444, dcon_mode_show, NULL),
+	__ATTR(sleep, 0644, dcon_sleep_show, dcon_sleep_store),
+	__ATTR(source, 0644, dcon_source_show, dcon_source_store),
+	__ATTR(output, 0644, dcon_output_show, dcon_output_store),
+};
+
+static struct backlight_ops dcon_bl_ops = {
+	.get_brightness = dconbl_get,
+	.update_status = dconbl_set
+};
+
+/* List of GPIOs that we care about:
+   (in)  GPIO12   -- DCONBLNK
+   (in)  GPIO[56] -- DCONSTAT[01]
+   (out) GPIO11   -- DCONLOAD
+*/
+
+#define IN_GPIOS ((1<<5) | (1<<6) | (1<<7) | (1<<12))
+#define OUT_GPIOS (1<<11)
+
+static irqreturn_t dcon_interrupt(int, void *);
+
+static int dcon_request_irq(void)
+{
+	unsigned long lo, hi;
+	unsigned char lob;
+
+	rdmsr(MSR_LBAR_GPIO, lo, hi);
+
+	/* Check the mask and whether GPIO is enabled (sanity check) */
+	if (hi != 0x0000f001) {
+		printk(KERN_ERR "GPIO not enabled -- cannot use DCON\n");
+		return -ENODEV;
+	}
+
+	/* Mask off the IO base address */
+	gpio_base = lo & 0x0000ff00;
+
+	/* Turn off the event enable for GPIO7 just to be safe */
+	outl(1 << (16+7), gpio_base + GPIOx_EVNT_EN);
+
+	/* Set the directions for the GPIO pins */
+	outl(OUT_GPIOS | (IN_GPIOS << 16), gpio_base + GPIOx_OUT_EN);
+	outl(IN_GPIOS | (OUT_GPIOS << 16), gpio_base + GPIOx_IN_EN);
+
+	/* Set up the interrupt mappings first, so we can collect the
+	 * first interrupt when it happens
+	 */
+
+	hi = inl(gpio_base + GPIO_MAP_X);
+	hi &= 0x0fffffff;
+	hi |= 0x70000000;
+	outl(hi, gpio_base + GPIO_MAP_X);
+
+	/* Don't map the GPIO12 interrupt */
+
+	hi = inl(gpio_base + GPIO_MAP_Y);
+	hi &= 0xfff0ffff;
+	hi |= 0x00000000;
+	outl(hi, gpio_base + GPIO_MAP_Y);
+
+	/* Enable GPIO IRQ 7 to trigger the PIC interrupt in the Z sources */
+
+	rdmsr(0x51400023, hi, lo);
+	hi &= 0x0fffffff;
+	hi |= (DCON_IRQ << 28);
+	wrmsrl(0x51400023, hi);
+
+	/* Select edge level for interrupt (in PIC) */
+
+	lob = inb(0x4d0);
+	lob &= ~(1 << DCON_IRQ);
+	outb(lob, 0x4d0);
+
+	/* Register the interupt handler */
+	if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver))
+		return -EIO;
+
+	/* Clear INV_EN for GPIO7 (DCONIRQ) */
+	outl((1<<(16+7)), gpio_base + GPIOx_INV_EN);
+
+	/* Enable filter for GPIO12 (DCONBLANK) */
+	outl(1<<(12), gpio_base + GPIOx_IN_FLTR_EN);
+
+	/* Disable filter for GPIO7 */
+	outl(1<<(16+7), gpio_base + GPIOx_IN_FLTR_EN);
+
+	/* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
+
+	outl(1<<(16+7), gpio_base + GPIOx_EVNTCNT_EN);
+	outl(1<<(16+12), gpio_base + GPIOx_EVNTCNT_EN);
+
+	/* Add GPIO12 to the Filter Event Pair #7 */
+	outb(12, gpio_base + GPIO_FE7_SEL);
+
+	/* Turn off negative Edge Enable for GPIO12 */
+	outl(1<<(16+12), gpio_base + GPIOx_NEGEDGE_EN);
+
+	/* Enable negative Edge Enable for GPIO7 */
+	outl(1<<7, gpio_base + GPIOx_NEGEDGE_EN);
+
+	/* Zero the filter amount for Filter Event Pair #7 */
+	outw(0, gpio_base + GPIO_FLT7_AMNT);
+
+	/* Clear the negative edge status for GPIO7 and GPIO12 */
+	outl((1<<7) | (1<<12), gpio_base+0x4c);
+
+	/* FIXME:  Clear the posiitive status as well, just to be sure */
+	outl((1<<7) | (1<<12), gpio_base+0x48);
+
+	/* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
+	outl((1<<(7))|(1<<12), gpio_base + GPIOx_EVNT_EN);
+
+	/* Assert DCONLOAD - this asserts that the CPU is still in control */
+	outl(1<<11, gpio_base + GPIOx_OUT_VAL);
+	return 0;
+}
+
+static int dcon_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct i2c_client *client;
+	struct pci_dev *dev;
+	uint16_t ver;
+	int rc, i;
+
+	if (!olpc_has_dcon()) {
+		printk("olpc-dcon:  No DCON is attached.\n");
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_FB_GEODE_GX
+	dev = pci_get_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_GX_VIDEO, NULL);
+	if (dev)
+		fbinfo = pci_get_drvdata(dev);
+#endif
+
+#ifdef CONFIG_FB_GEODE_LX
+	if (!fbinfo) {
+		dev = pci_get_device(PCI_VENDOR_ID_AMD,
+					PCI_DEVICE_ID_AMD_LX_VIDEO, NULL);
+		if (dev)
+			fbinfo = pci_get_drvdata(dev);
+	}
+#endif
+
+	if (!fbinfo) {
+		printk(KERN_ERR "dcon:  Couldn't find a Geode GPU device\n");
+		return -ENXIO;
+	}
+
+	if (adap->id != I2C_HW_SMBUS_SCX200) {
+		printk(KERN_ERR "olpc-dcon: Invalid I2C bus (%d not %d)\n",
+		       adap->id, I2C_HW_SMBUS_SCX200);
+		return -ENXIO;
+	}
+
+	client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+
+	strncpy(client->name, "OLPC-DCON", I2C_NAME_SIZE);
+	client->addr = addr;
+	client->adapter = adap;
+	client->driver = &dcon_driver;
+
+	if ((rc = i2c_attach_client(client)) != 0) {
+		printk(KERN_ERR "olpc-dcon: Unable to attach the I2C client.\n");
+		goto eclient;
+	}
+
+	ver = i2c_smbus_read_word_data(client, DCON_REG_ID);
+
+	if ((ver >> 8) != 0xDC) {
+		printk(KERN_ERR "olpc-dcon: DCON ID not 0xDCxx: 0x%04x instead.\n", ver);
+		rc = -ENXIO;
+		goto ei2c;
+	}
+
+	if ((rc = dcon_request_irq())) {
+		printk(KERN_ERR "olpc-dcon: Unable to grab IRQ.\n");
+		goto ei2c;
+	}
+
+	if (ver < 0xdc02 && !noinit) {
+		/* Initialize the DCON registers */
+
+		/* Start with work-arounds for DCON ASIC */
+		i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
+		i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
+		i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
+		i2c_smbus_write_word_data(client, 0x0b, 0x007a);
+		i2c_smbus_write_word_data(client, 0x36, 0x025c);
+		i2c_smbus_write_word_data(client, 0x37, 0x025e);
+		
+		/* Initialise SDRAM */
+
+		i2c_smbus_write_word_data(client, 0x3b, 0x002b);
+		i2c_smbus_write_word_data(client, 0x41, 0x0101);
+		i2c_smbus_write_word_data(client, 0x42, 0x0101);
+	}
+
+	/* Colour swizzle, AA, no passthrough, backlight */
+
+	dcon_disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE | MODE_CSWIZZLE;
+	if (useaa)
+		dcon_disp_mode |= MODE_COL_AA;
+	i2c_smbus_write_word_data(client, DCON_REG_MODE, dcon_disp_mode);
+
+
+	/* Set the scanline to interrupt on during resume */
+
+	i2c_smbus_write_word_data(client, DCON_REG_SCAN_INT, resumeline);
+
+	/* Add the DCON device */
+
+	dcon_device = platform_device_alloc("dcon", -1);
+
+	if (dcon_device == NULL) {
+		printk(KERN_ERR "dcon:  Unable to create the DCON device\n");
+		rc = -ENOMEM;
+		goto eirq;
+	}
+
+	if ((rc = platform_device_add(dcon_device))) {
+		printk(KERN_ERR "dcon:  Unable to add the DCON device\n");
+		goto edev;
+	}
+
+	for(i = 0; i < ARRAY_SIZE(dcon_device_files); i++)
+		device_create_file(&dcon_device->dev, &dcon_device_files[i]);
+
+	/* Add the backlight device for the DCON */
+
+	dcon_client = client;
+
+	dcon_bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev,
+		NULL, &dcon_bl_ops);
+
+	if (IS_ERR(dcon_bl_dev)) {
+		printk(KERN_INFO "Could not register the backlight device for the DCON (%ld)\n", PTR_ERR(dcon_bl_dev));
+		dcon_bl_dev = NULL;
+	}
+	else {
+		dcon_bl_dev->props.max_brightness = 15;
+		dcon_bl_dev->props.power = FB_BLANK_UNBLANK;
+		dcon_bl_dev->props.brightness = dcon_get_backlight();
+
+		backlight_update_status(dcon_bl_dev);
+	}
+
+	printk(KERN_INFO "olpc-dcon: Discovered DCON version %x\n", ver & 0xFF);
+
+	return 0;
+
+ edev:
+	platform_device_unregister(dcon_device);
+	dcon_device = NULL;
+ eirq:
+	free_irq(DCON_IRQ, &dcon_driver);
+ ei2c:
+	i2c_detach_client(client);
+ eclient:
+	kfree(client);
+
+	return rc;
+}
+
+static int dcon_attach(struct i2c_adapter *adap)
+{
+	int ret;
+
+	ret = i2c_probe(adap, &addr_data, dcon_probe);
+
+	if (dcon_client == NULL)
+		printk(KERN_ERR "olpc-dcon: No DCON found on SMBus\n");
+
+	return ret;
+}
+
+static int dcon_detach(struct i2c_client *client)
+{
+	int rc;
+	dcon_client = NULL;
+
+	free_irq(DCON_IRQ, &dcon_driver);
+
+	if ((rc = i2c_detach_client(client)) == 0)
+		kfree(i2c_get_clientdata(client));
+
+	if (dcon_bl_dev != NULL)
+		backlight_device_unregister(dcon_bl_dev);
+
+	if (dcon_device != NULL)
+		platform_device_unregister(dcon_device);
+
+	return rc;
+}
+
+static irqreturn_t dcon_interrupt(int irq, void *id)
+{
+	int status = inl(gpio_base + GPIOx_READ_BACK) >> 5;
+
+	/* Clear the negative edge status for GPIO7 */
+	outl(1 << 7, gpio_base + GPIOx_NEGEDGE_STS);
+
+	switch (status & 3) {
+	case 3:
+		printk(KERN_DEBUG "olpc-dcon: DCONLOAD_MISSED interrupt\n");
+		break;
+	case 2:	/* switch to DCON mode */
+	case 1: /* switch to CPU mode */
+		dcon_switched = 1;
+		wake_up(&dcon_wait_queue);
+		break;
+	case 0:
+		printk(KERN_DEBUG "olpc-dcon: scanline interrupt w/CPU\n");
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int dcon_reboot_notify(struct notifier_block *nb, unsigned long foo, void *bar)
+{
+	if (dcon_client == NULL)
+		return 0;
+
+	/* Turn off the DCON. Entirely. */
+	dcon_write(DCON_REG_MODE, 0x39);
+	dcon_write(DCON_REG_MODE, 0x32);
+	return 0;
+}
+
+static struct i2c_driver dcon_driver = {
+	.driver = {
+		.name	= "OLPC-DCON",
+	},
+	.id = I2C_DRIVERID_DCON,
+	.attach_adapter = dcon_attach,
+	.detach_client = dcon_detach,
+};
+
+static struct notifier_block dcon_nb = {
+	.notifier_call = dcon_reboot_notify,
+	.priority = -1,
+};
+
+static int __init olpc_dcon_init(void)
+{
+	i2c_add_driver(&dcon_driver);
+	register_reboot_notifier(&dcon_nb);
+	return 0;
+}
+
+static void __exit olpc_dcon_exit(void)
+{
+	unregister_reboot_notifier(&dcon_nb);
+	i2c_del_driver(&dcon_driver);
+}
+
+module_init(olpc_dcon_init);
+module_exit(olpc_dcon_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/olpc_dcon.h b/drivers/video/olpc_dcon.h
new file mode 100644
index 0000000..e758d79
--- /dev/null
+++ b/drivers/video/olpc_dcon.h
@@ -0,0 +1,75 @@
+#ifndef GXFB_DCON_H_
+#define GXFB_DCON_H_
+
+/* DCON registers */
+
+#define DCON_REG_ID		 0
+#define DCON_REG_MODE		 1
+
+#define MODE_PASSTHRU	(1<<0)
+#define MODE_SLEEP	(1<<1)
+#define MODE_SLEEP_AUTO	(1<<2)
+#define MODE_BL_ENABLE	(1<<3)
+#define MODE_BLANK	(1<<4)
+#define MODE_CSWIZZLE	(1<<5)
+#define MODE_COL_AA	(1<<6)
+#define MODE_MONO_LUMA	(1<<7)
+#define MODE_SCAN_INT	(1<<8)
+#define MODE_CLOCKDIV	(1<<9)
+#define MODE_DEBUG	(1<<14)
+#define MODE_SELFTEST	(1<<15)
+
+#define DCON_REG_HRES		2
+#define DCON_REG_HTOTAL		3
+#define DCON_REG_HSYNC_WIDTH	4
+#define DCON_REG_VRES		5
+#define DCON_REG_VTOTAL		6
+#define DCON_REG_VSYNC_WIDTH	7
+#define DCON_REG_TIMEOUT	8
+#define DCON_REG_SCAN_INT	9
+#define DCON_REG_BRIGHT		10
+
+/* GPIO registers (CS5536) */
+
+#define MSR_LBAR_GPIO		0x5140000C
+
+#define GPIOx_OUT_VAL     0x00
+#define GPIOx_OUT_EN      0x04
+#define GPIOx_IN_EN       0x20
+#define GPIOx_INV_EN      0x24
+#define GPIOx_IN_FLTR_EN  0x28
+#define GPIOx_EVNTCNT_EN  0x2C
+#define GPIOx_READ_BACK   0x30
+#define GPIOx_EVNT_EN     0x38
+#define GPIOx_NEGEDGE_EN  0x44
+#define GPIOx_NEGEDGE_STS 0x4C
+#define GPIO_FLT7_AMNT    0xD8
+#define GPIO_MAP_X        0xE0
+#define GPIO_MAP_Y        0xE4
+#define GPIO_FE7_SEL      0xF7
+
+
+/* Status values */
+
+#define DCONSTAT_SCANINT	0
+#define DCONSTAT_SCANINT_DCON	1
+#define DCONSTAT_DISPLAYLOAD	2
+#define DCONSTAT_MISSED		3
+
+/* Source values */
+
+#define DCON_SOURCE_DCON        0
+#define DCON_SOURCE_CPU         1
+
+/* Output values */
+#define DCON_OUTPUT_COLOR       0
+#define DCON_OUTPUT_MONO        1
+
+/* Sleep values */
+#define DCON_ACTIVE             0
+#define DCON_SLEEP              1
+
+/* Interrupt */
+#define DCON_IRQ                6
+
+#endif





More information about the Devel mailing list