[PATCH] DCON: Add the DCON platform device

Jordan Crouse jordan.crouse at amd.unroutablecom
Wed Nov 29 11:14:54 EST 2006


Commit:     aca4163a73dc442bfe24ab1e1462513504c162df
Parent:     89ca0c5a674fcf78fd87bb541e75ae3139422ecb
commit aca4163a73dc442bfe24ab1e1462513504c162df
Author:     Jordan Crouse <jordan.crouse at amd.com>
AuthorDate: Thu Oct 5 16:51:32 2006 -0600
Commit:     Jordan Crouse <jordan.crouse at amd.com>
CommitDate: Fri Oct 6 09:26:07 2006 -0600

    [PATCH] DCON:  Add the DCON platform device
---
 arch/i386/kernel/olpc.c           |    4 
 drivers/video/backlight/dcon_bl.c |    6 -
 drivers/video/geode/gxfb_core.c   |   17 +
 drivers/video/geode/gxfb_dcon.c   |  444 +++++++++++++++++++++++--------------
 drivers/video/geode/gxfb_dcon.h   |   12 +
 5 files changed, 297 insertions(+), 186 deletions(-)

diff --git a/arch/i386/kernel/olpc.c b/arch/i386/kernel/olpc.c
index ffa4823..33ac22d 100644
--- a/arch/i386/kernel/olpc.c
+++ b/arch/i386/kernel/olpc.c
@@ -1,4 +1,4 @@
-/* Support for the OLPC Childrens Machine
+/* Support for the OLPC DCON
  * Copyright (C) 2006, Advanced Micro Devices, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@ static int __init olpc_init(void) {
 
   unsigned char val;
 
-  /* Read the DCON present bit and set the flag accordingly */
+  /* Read the DCON present bit in the CMOS and set the flag accordingly */
 
   val = CMOS_READ(OLPC_CMOS_DCON_OFFSET);
   olpc_dcon_present = (val & OLPC_CMOS_DCON_MASK);
diff --git a/drivers/video/backlight/dcon_bl.c b/drivers/video/backlight/dcon_bl.c
index d356c36..78403da 100644
--- a/drivers/video/backlight/dcon_bl.c
+++ b/drivers/video/backlight/dcon_bl.c
@@ -27,12 +27,12 @@ static int dconbl_set(struct backlight_d
 	if (power != FB_BLANK_UNBLANK)
 	  level = 0;
 
-	gxfb_dcon_setbl(level);
+	dcon_set_backlight(level);
 	return 0;
 }
 
 static int dconbl_get(struct backlight_device *dev) {
-	return gxfb_dcon_getbl();
+	return dcon_get_backlight();
 }
 
 static struct backlight_properties dcon_data = {
@@ -49,7 +49,7 @@ static int dconbl_probe(struct platform_
 	if (IS_ERR(dconbl_dev))
 		return PTR_ERR(dconbl_dev);
 	
-	dcon_data.brightness = gxfb_dcon_getbl();
+	dcon_data.brightness = dcon_get_backlight();
 	return 0;
 }
 
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index ab14dda..7617e3d 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -121,8 +121,6 @@ static const struct fb_videomode gx_dcon
 extern int olpc_dcon_present;
 #endif
 
-unsigned long gxfb_dc_regs;
-EXPORT_SYMBOL(gxfb_dc_regs);
 
 static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
@@ -249,8 +247,6 @@ static int __init gxfb_map_video_memory(
 	if (!par->dc_regs)
 		return -ENOMEM;
 
-	gxfb_dc_regs = par->dc_regs;
-
 	ret = pci_request_region(dev, 0, "gxfb (framebuffer)");
 	if (ret < 0)
 		return ret;
@@ -281,13 +277,14 @@ static int __init gxfb_map_video_memory(
 	return 0;
 }
 
-int (*gxfb_ioctl_func)(struct fb_info *info, unsigned int cmd, unsigned long arg);
+int (*gxfb_ioctl_func)(struct fb_info *, unsigned int, unsigned long);
 EXPORT_SYMBOL(gxfb_ioctl_func);
 
-gxfb_ioctl( struct fb_info *info, unsigned int cmd, unsigned long arg) {
-  
+static int gxfb_ioctl( struct fb_info *info, unsigned int cmd,
+		       unsigned long arg)
+{
 	int ret = -ENOTTY;
-  
+
 	if (gxfb_ioctl_func != NULL)
 		ret = gxfb_ioctl_func(info, cmd, arg);
 
@@ -346,7 +343,8 @@ static struct fb_info * __init gxfb_init
 	return info;
 }
 
-static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int __init gxfb_probe(struct pci_dev *pdev,
+			     const struct pci_device_id *id)
 {
 	struct geodefb_par *par;
 	struct fb_info *info;
@@ -359,6 +357,7 @@ static int __init gxfb_probe(struct pci_
 	info = gxfb_init_fbinfo(&pdev->dev);
 	if (!info)
 		return -ENOMEM;
+
 	par = info->par;
 
 	/* GX display controller and GX video device. */
diff --git a/drivers/video/geode/gxfb_dcon.c b/drivers/video/geode/gxfb_dcon.c
index ec1489a..e745e89 100644
--- a/drivers/video/geode/gxfb_dcon.c
+++ b/drivers/video/geode/gxfb_dcon.c
@@ -1,6 +1,6 @@
 /*
  *  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
@@ -16,9 +16,13 @@ #include "geodefb.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/device.h>
 #include <asm/uaccess.h>
+#include <linux/ctype.h>
 #include <asm/tsc.h>
 
 #include "gxfb_dcon.h"
@@ -47,176 +51,167 @@ static struct i2c_driver dcon_driver;
 static struct i2c_client *dcon_client;
 
 /* Platform devices */
-static struct platform_device *dcon_bl_dev;
+static struct platform_device *dcon_device;
+static struct platform_device *dconbl_device;
 
 /* 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 = DCON_ACTIVE;
+
 /* Shadow register for the DCON_REG_MODE register */
 static unsigned short dcon_disp_mode;
 
 /* Variables used during switches */
-int dcon_waiting;
+static int dcon_switched;
 
-static DECLARE_WAIT_QUEUE_HEAD(dcon_mode_wq);
-extern unsigned long gxfb_dc_regs;
+static DECLARE_WAIT_QUEUE_HEAD(dcon_wait_queue);
 
 extern int gxfb_powerdown(struct fb_info *info);
 extern int gxfb_powerup(struct fb_info *info);
 
+extern int (*gxfb_ioctl_func)(struct fb_info *, unsigned int, unsigned long);
+
+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)
 
 /* ===== API functions - these are called by a variety of users ==== */
 
+/* The current backlight value - this saves us some smbus traffic */
 static int gxfb_bl_val = -1;
 
-/* To cut down on SMBUS, we assume that getbl and setbl are the only entities that change the BL,
-   so we shadow the variable
-*/
-
-int gxfb_dcon_getbl(void) {
+/* 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.
+ */
 
-  if (dcon_client == NULL)
-    return 0;
+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;
+	if (gxfb_bl_val == -1)
+		gxfb_bl_val = dcon_read(DCON_REG_BRIGHT) & 0x0F;
 
-  return (dcon_disp_mode & MODE_BL_ENABLE) ? gxfb_bl_val : 0;
+	return gxfb_bl_val;
 }
 
-void gxfb_dcon_setbl(int level) {
+void dcon_set_backlight(int level)
+{
+	if (dcon_client == NULL)
+		return;
 
-  if (dcon_client == NULL)
-    return;
+	if (gxfb_bl_val == (level & 0x0F))
+		return;
 
-  if (level == 0) {
-	  if ((dcon_disp_mode & MODE_BL_ENABLE)) {
-		  dcon_disp_mode &= ~MODE_BL_ENABLE;
-		  dcon_write(DCON_REG_MODE, dcon_disp_mode);
-	  }
+	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;
 
-	  return;
-  }
+	dcon_output = arg;
 
-  if (!(dcon_disp_mode & MODE_BL_ENABLE)) {
-	  dcon_disp_mode |= MODE_BL_ENABLE;
-	  dcon_write(DCON_REG_MODE, dcon_disp_mode);
-  }
+	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 | MODE_COL_AA);
+	}
 
-  gxfb_bl_val = level & 0x0F;
-  dcon_write(DCON_REG_BRIGHT, gxfb_bl_val);
+	dcon_write(DCON_REG_MODE, dcon_disp_mode);
+	return 0;
 }
 
-EXPORT_SYMBOL(gxfb_dcon_getbl);
-EXPORT_SYMBOL(gxfb_dcon_setbl);
+EXPORT_SYMBOL(dcon_get_backlight);
+EXPORT_SYMBOL(dcon_set_backlight);
 
-static int dcon_set_source(struct fb_info *info, int arg)
-{
-	struct geodefb_par *par = info->par;
+/* Set the source of the display (CPU or DCON) */
 
+static int dcon_set_source(int arg)
+{
 	if (dcon_source == arg)
 		return 0;
 
 	dcon_source = arg;
 
+	DECLARE_WAITQUEUE(wait, current);
+
+	dcon_switched = 0;
+
 	if (arg == DCON_SOURCE_CPU) {
-		unsigned long timeo = jiffies + (10 * HZ);
-		unsigned long scanline1, scanline2;
 
-		/* Power up the graphics engine */
-		gxfb_powerup(info);
+		/* FIXME:  Set the task uninterruptable for this bit? */
 
-		/* Wait for up to a second for our output to coincide with
-		   DCON's */
+		/* Enable the scanline interrupt bit */
+		dcon_write(DCON_REG_MODE, dcon_disp_mode | MODE_SCAN_INT);
 
-		while (time_before(jiffies, timeo)) {
-			unsigned long dconblnk =
-				(inl(gpio_base + GPIOx_READ_BACK) >> 12) & 1;
-			scanline1 =
-				readl((void __iomem *)(gxfb_dc_regs + 0x6c));
-			scanline1 >>= 16;
-			scanline1 &= 0x7ff;
+		/* Wait up to one second for the mode to change */
+		wait_event_timeout(dcon_wait_queue, dcon_switched == 1, HZ);
 
-			if (!dconblnk && scanline1 >= resumeline &&
-			    scanline1 <= (resumeline+2))
-				goto ready;
+		/* Turn off the scanline interrupt */
+		dcon_write(DCON_REG_MODE, dcon_disp_mode);
 
-#if 0
-			if (!dconblnk)
-				printk("Not ready. blnk %ld, line %ld\n",
-				       dconblnk, scanline1);
-#endif
+		if (!dcon_switched) {
+			printk(KERN_ERR "dcon:  Timeout entering CPU mode\n");
+			return -1;
 		}
 
-		printk("Wait for VGA ready timed out\n");
-ready:
+		/* Turn on the graphics engine */
+		gxfb_powerup(fbinfo);
 		outl(1<<11, gpio_base + GPIOx_OUT_VAL);
-		scanline2 = readl((void __iomem *)(gxfb_dc_regs + 0x6c));
-		scanline2 >>= 16;
-		scanline2 &= 0x7ff;
-		printk("Re-enabled between %ld and %ld\n",
-				scanline1, scanline2);
 
-	} else {
+		printk(KERN_INFO "dcon: The CPU has control\n");
+	}
+	else {
 		int t;
-		DECLARE_WAITQUEUE(wait, current);
 
-		add_wait_queue(&dcon_mode_wq, &wait);
+		add_wait_queue(&dcon_wait_queue, &wait);
 		set_current_state(TASK_UNINTERRUPTIBLE);
-		dcon_waiting = 1;
 
-		/* Clear GPIO11 (DCONLOAD) - this implies that the DCON is in 
+		/* 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_mode_wq, &wait);
+		remove_wait_queue(&dcon_wait_queue, &wait);
 		set_current_state(TASK_RUNNING);
 
-		if (dcon_waiting) {
-			printk("Timeout entering DCON mode\n");
+		if (!dcon_switched) {
+			printk(KERN_ERR "dcon: Timeout entering DCON mode\n");
 
-			/* Re-enable GPIO11 - we never gave up control */
+			/* Set DCONLOAD back to CPU control */
 			outl(1 << 11, gpio_base + GPIOx_OUT_VAL);
-
 			return -1;
 		}
 
 		/* Turn off the graphics engine completely */
-		gxfb_powerdown(info);
-	}
-
-	return 0;
-}
-
-static int dcon_set_output(struct fb_info *info, 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 | MODE_COL_AA);
+		gxfb_powerdown(fbinfo);
+		printk(KERN_INFO "dcon: The DCON has control\n");
 	}
 
-	i2c_smbus_write_word_data(dcon_client, DCON_REG_MODE, dcon_disp_mode);
-
+	dcon_source = arg;
 	return 0;
 }
 
@@ -236,9 +231,9 @@ static int gxfb_dcon_ioctl(struct fb_inf
 			return -EINVAL;
 
 		if (karg > -1)
-			ret = dcon_set_source(info, karg);
+			ret = dcon_set_source(karg);
 
-		if (ret) 
+		if (ret)
 			karg = -1;
 		else
 			karg = dcon_source;
@@ -254,9 +249,9 @@ static int gxfb_dcon_ioctl(struct fb_inf
 			return -EINVAL;
 
 		if (karg > -1)
-			ret = dcon_set_output(info, karg);
+			ret = dcon_set_output(karg);
 
-		if (ret) 
+		if (ret)
 			return ret;
 
 		karg = dcon_output;
@@ -293,11 +288,11 @@ static int gxfb_dcon_ioctl(struct fb_inf
 		karg = 0;
 		break;
 
-	case DCONIOC_SETBL:	  
+	case DCONIOC_SETBL:
 		if (get_user(karg, (int __user *)arg))
 			return -EFAULT;
 
-		gxfb_dcon_setbl(karg);
+		dcon_set_backlight(karg);
 		karg = 0;
 		break;
 
@@ -305,7 +300,7 @@ static int gxfb_dcon_ioctl(struct fb_inf
 		if (get_user(karg, (int __user *)arg))
 			return -EFAULT;
 
-		karg = gxfb_dcon_getbl();
+		karg = dcon_get_backlight();
 		break;
 
 	default:
@@ -316,22 +311,123 @@ static int gxfb_dcon_ioctl(struct fb_inf
 	return ret;
 }
 
-static unsigned short normal_i2c[] = { 0x0D, I2C_CLIENT_END };
-I2C_CLIENT_INSMOD;
+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);
+}
+
+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;
+}
 
-extern int (*gxfb_ioctl_func)(struct fb_info *info, unsigned int cmd,
-		       unsigned long arg);
+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;
+
+	if (output == DCON_SOURCE_CPU || output == DCON_SOURCE_DCON) {
+		dcon_set_source(output);
+		rc = count;
+	}
+
+	return rc;
+}
+
+#if 0
+/* FIXME:  I haven't yet implemented sleep support */
 
+static ssize_t dcon_sleep_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_sleep(output ? DCON_SLEEP : DCON_ACTIVE);
+	return count;
+}
+#endif
+
+static struct device_attribute dcon_device_files[] = {
+	__ATTR(mode, 0444, dcon_mode_show, NULL),
+	__ATTR(sleep, 0644, dcon_sleep_show, NULL),
+	__ATTR(source, 0644, dcon_source_show, dcon_source_store),
+	__ATTR(output, 0644, dcon_output_show, dcon_output_store),
+};
 
 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;
+	int rc, i;
+
+	dev = pci_find_device(PCI_VENDOR_ID_NS,PCI_DEVICE_ID_NS_GX_VIDEO,
+			      NULL);
+
+	if (dev == NULL || (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);
+		       adap->id, I2C_HW_SMBUS_SCX200);
 		return -ENXIO;
 	}
 
@@ -346,25 +442,17 @@ static int dcon_probe(struct i2c_adapter
 
 	if ((rc = i2c_attach_client(client)) != 0) {
 		printk(KERN_ERR "gxfb-dcon: Unable to attach the I2C client.\n");
-		kfree(client);
-		return rc;
+		goto eclient;
 	}
 
 	ver = i2c_smbus_read_word_data(client, DCON_REG_ID);
 
 	if ((ver >> 8) != 0xDC) {
-		/* Not a DCON chip? */
 		printk(KERN_ERR "gxfb-dcon: DCON ID not 0xDCxx: 0x%04x instead.\n", ver);
-		i2c_detach_client(client);
-		kfree(client);
-		return -ENXIO;
+		rc = -ENXIO;
+		goto ei2c;
 	}
 
-	printk(KERN_INFO "gxfb-dcon: Discovered DCON version %x\n", ver & 0xFF);
-
-	dcon_client = client;
-	gxfb_ioctl_func = gxfb_dcon_ioctl;
-
 	if (!noinit) {
 		/* Initialize the DCON registers */
 
@@ -377,7 +465,7 @@ static int dcon_probe(struct i2c_adapter
 			MODE_CSWIZZLE | MODE_COL_AA;
 
 		i2c_smbus_write_word_data(client, DCON_REG_MODE,
-				dcon_disp_mode);
+					  dcon_disp_mode);
 
 		/* Initialise SDRAM */
 
@@ -393,22 +481,62 @@ static int dcon_probe(struct i2c_adapter
 		i2c_smbus_write_word_data(client, 0x43, 0x0101);
 	}
 
-	/* Now create a backlight device on the platform bus - note that failures here are non fatal */
+	/* Set the scanline to interrupt on during resume */
 
-	dcon_bl_dev = platform_device_alloc("dcon-bl", -1);
+	i2c_smbus_write_word_data(client, DCON_REG_SCAN_INT, resumeline);
 
-	if (dcon_bl_dev == NULL) {
-		printk(KERN_INFO "gxfb-dcon: Couldn't allocate the BL device.\n");
-		return 0;
+	/* 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 ei2c;
+	}
+
+	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 DCON backlight device (for use by the backlight engine) */
+
+	dconbl_device = platform_device_alloc("dcon-bl", -1);
+
+	if (dconbl_device == NULL) {
+		printk(KERN_ERR "dcon:  Unable to create the DCON backlight device\n");
+		rc = -ENOMEM;
+		goto edev;
 	}
 
-	if (platform_device_add(dcon_bl_dev)) {
-		printk(KERN_INFO "gxfb-dcon: Couldn't attach the BL device.\n");
-		platform_device_put(dcon_bl_dev);
-		dcon_bl_dev = NULL;
+	if ((rc = platform_device_add(dconbl_device))) {
+		printk(KERN_ERR "dcon:  Unable to add the DCON backlight device\n");
+		goto ebldev;
 	}
 
+	dcon_client = client;
+	gxfb_ioctl_func = gxfb_dcon_ioctl;
+
+	printk(KERN_INFO "gxfb-dcon: Discovered DCON version %x\n", ver & 0xFF);
+
 	return 0;
+
+ ebldev:
+	platform_device_unregister(dconbl_device);
+	dconbl_device = NULL;
+ edev:
+	platform_device_unregister(dcon_device);
+	dcon_device = NULL;
+ ei2c:
+	i2c_detach_client(client);
+ eclient:
+	kfree(client);
+
+	return rc;
 }
 
 static int dcon_attach(struct i2c_adapter *adap)
@@ -432,8 +560,11 @@ static int dcon_detach(struct i2c_client
 	if ((rc = i2c_detach_client(client)) == 0)
 		kfree(i2c_get_clientdata(client));
 
-	if (dcon_bl_dev != NULL)
-		platform_device_unregister(dcon_bl_dev);
+	if (dconbl_device != NULL)
+		platform_device_unregister(dconbl_device);
+
+	if (dcon_device != NULL)
+		platform_device_unregister(dcon_device);
 
 	return rc;
 }
@@ -448,45 +579,27 @@ static struct i2c_driver dcon_driver = {
 };
 
 
-
-int dcon_interrupt(int irq, void *id, struct pt_regs *regs)
+static int dcon_interrupt(int irq, void *id, struct pt_regs *regs)
 {
-	unsigned long gpios = inl(gpio_base + GPIOx_READ_BACK);
-	int dconstat = (gpios >> 5) & 3;
-	int dconblnk = (gpios >> 12) & 1;
-	cycles_t foo = get_cycles();
-	static cycles_t foo1;
-	static int lastec7;
-
-	unsigned long scanline = readl((void __iomem *)(gxfb_dc_regs + 0x6c));
-	int ec7 = inl(gpio_base + 0xDC);
-	int dconirq = (gpios >> 7) & 1;
-
-	scanline >>= 16;
-	scanline &= 0x7ff;
-	printk("IRQ! TSC %08lx (+%ld), line %ld, STAT %d BLNK %d IRQ %d EC7 %d:+%d\n",
-	       (long)foo, (long)(foo-foo1), scanline, dconstat, dconblnk, dconirq, ec7, ec7-lastec7);
-	foo1 = foo;
-	lastec7 = ec7;
+	unsigned long gin = inl(gpio_base + GPIOx_READ_BACK);
+	int dconstat = (gin >> 5) & 3;
 
 	/* Clear the negative edge status for GPIO7 */
 	outl(1 << 7, gpio_base + GPIOx_NEGEDGE_STS);
 
-#if 0
-	printk("GPIOL_NEGEDGE_STS[7] is %d\n", (inl(gpio_base+0x4c)>>7) & 1);
+	if (dconstat == 2 || dconstat == 1) {
+		if (dconstat == 2)
+			printk("IRQ - switch to DCON mode\n");
+		else
+			printk("IRQ - switch to CPU mode\n");
 
-	outl(1<<7, gpio_base+0x4c);
-	outl(1<<(16+7), gpio_base + GPIOx_EVNTCNT_EN);
-	outl(1<<(7), gpio_base + GPIOx_EVNTCNT_EN);
-#endif
-	if (dconstat == 2) {
-		dcon_waiting = 0;
-		wake_up(&dcon_mode_wq);
+		dcon_switched = 1;
+		wake_up(&dcon_wait_queue);
 	}
+
 	return IRQ_HANDLED;
 }
 
-
 /* List of GPIOs that we care about:
    (in)  GPIO12   -- DCONBLNK
    (in)  GPIO[56] -- DCONSTAT[01]
@@ -505,7 +618,7 @@ int __init gxfb_dcon_init(void)
 
 	/* Check the mask and whether GPIO is enabled (sanity check) */
 	if (hi != 0x0000f001) {
-		printk(KERN_WARNING "GPIO not enabled -- cannot use DCON\n");
+		printk(KERN_ERR "GPIO not enabled -- cannot use DCON\n");
 		return -ENODEV;
 	}
 
@@ -521,7 +634,7 @@ int __init gxfb_dcon_init(void)
 	outl(1 << (16 + 7), gpio_base + GPIOx_EVNT_EN);
 
 	/* Set up the interrupt mappings first, so we can collect the
-	 * first interrupt when it happens 
+	 * first interrupt when it happens
 	 */
 
 	hi = inl(gpio_base + GPIO_MAP_X);
@@ -533,7 +646,7 @@ int __init gxfb_dcon_init(void)
 
 	hi = inl(gpio_base + GPIO_MAP_Y);
 	hi &= 0xfff0ffff;
-	hi |= 0x00000000;	
+	hi |= 0x00000000;
 	outl(hi, gpio_base + GPIO_MAP_Y);
 
 	/* Enable GPIO IRQ 7 to trigger the PIC interrupt in the Z sources */
@@ -606,4 +719,3 @@ 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
index 140f2f0..522bd7a 100644
--- a/drivers/video/geode/gxfb_dcon.h
+++ b/drivers/video/geode/gxfb_dcon.h
@@ -65,15 +65,15 @@ #define DCON_SOURCE_CPU         1
 #define DCON_OUTPUT_COLOR       0
 #define DCON_OUTPUT_MONO        1
 
+/* Sleep values */
+#define DCON_ACTIVE             0
+#define DCON_SLEEPING           1
+
 /* Interrupt */
 #define DCON_IRQ                6
 
-/* Functions available to the world */
-
-/* Set the backlight */
-void gxfb_dcon_setbl(int);
 
-/* Get the backlight value */
-int gxfb_dcon_getbl(void);
+void dcon_set_backlight(int);
+int dcon_get_backlight(void);
 
 #endif


More information about the Commits-kernel mailing list