[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