[PATCH] Add DCON support to the GX framebuffer driver
Jordan Crouse
jordan.crouse at amd.com
Tue Nov 7 22:37:11 EST 2006
Commit: 75588273ae499a26d01f6a0a828306b1cb5d34c3
Parent: 21330ae883083e08be81fe4732d969d9eb5e97e9
commit 75588273ae499a26d01f6a0a828306b1cb5d34c3
Author: Jordan Crouse <jordan.crouse at amd.com>
AuthorDate: Thu Sep 21 16:39:54 2006 -0600
Commit: Jordan Crouse <jordan.crouse at amd.com>
CommitDate: Tue Oct 3 13:48:52 2006 -0600
[PATCH] Add DCON support to the GX framebuffer driver
This patch adds support for the OLPC DCON controller.
Original code by David Woodhouse, additional code by Jordan Crouse
---
drivers/video/geode/Kconfig | 9 +
drivers/video/geode/Makefile | 2
drivers/video/geode/gxfb_core.c | 25 ++
drivers/video/geode/gxfb_dcon.c | 542 +++++++++++++++++++++++++++++++++++++++
drivers/video/geode/gxfb_dcon.h | 71 +++++
drivers/video/geode/video_gx.c | 81 ++++++
drivers/video/geode/video_gx.h | 2
include/linux/i2c-id.h | 1
8 files changed, 729 insertions(+), 4 deletions(-)
diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig
index 4e173ef..60791ea 100644
--- a/drivers/video/geode/Kconfig
+++ b/drivers/video/geode/Kconfig
@@ -23,6 +23,15 @@ config FB_GEODE_GX
If unsure, say N.
+config FB_GEODE_GX_DCON
+ tristate "One Laptop Per Child Display CONtroller support"
+ depends on FB_GEODE_GX
+ 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_GEODE_GX1
tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)"
depends on FB && FB_GEODE && EXPERIMENTAL
diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile
index f896565..390a97d 100644
--- a/drivers/video/geode/Makefile
+++ b/drivers/video/geode/Makefile
@@ -3,5 +3,7 @@ # Makefile for the Geode family framebuf
obj-$(CONFIG_FB_GEODE_GX1) += gx1fb.o
obj-$(CONFIG_FB_GEODE_GX) += gxfb.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
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index 0f5b068..ab14dda 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -121,6 +121,9 @@ 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)
{
if (var->xres > 1600 || var->yres > 1200)
@@ -246,6 +249,8 @@ 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;
@@ -276,12 +281,26 @@ static int __init gxfb_map_video_memory(
return 0;
}
+int (*gxfb_ioctl_func)(struct fb_info *info, unsigned int cmd, unsigned long arg);
+EXPORT_SYMBOL(gxfb_ioctl_func);
+
+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);
+
+ return ret;
+}
+
static struct fb_ops gxfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = gxfb_check_var,
.fb_set_par = gxfb_set_par,
.fb_setcolreg = gxfb_setcolreg,
.fb_blank = gxfb_blank,
+ .fb_ioctl = gxfb_ioctl,
/* No HW acceleration for now. */
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
@@ -360,8 +379,9 @@ static int __init gxfb_probe(struct pci_
else
par->enable_crt = 1;
- /* If the OLPC DCON is present, then we use a special
- * mode database (don't say we support modes that we don't).
+ /* We need to determine a display mode right now, so we will
+ * check to see if the DCON was previously detected by the BIOS
+ * and use that to make our mode database decision.
*/
modedb_ptr = (struct fb_videomode *) gx_modedb;
@@ -369,7 +389,6 @@ static int __init gxfb_probe(struct pci_
#ifdef CONFIG_OLPC
if (olpc_dcon_present) {
- printk(KERN_INFO "gxfb: DCON detected.\n");
modedb_ptr = (struct fb_videomode *) gx_dcon_modedb;
modedb_size = ARRAY_SIZE(gx_dcon_modedb);
}
diff --git a/drivers/video/geode/gxfb_dcon.c b/drivers/video/geode/gxfb_dcon.c
new file mode 100644
index 0000000..e1eed59
--- /dev/null
+++ b/drivers/video/geode/gxfb_dcon.c
@@ -0,0 +1,542 @@
+/*
+ * 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 "geodefb.h"
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/tsc.h>
+
+#include "gxfb_dcon.h"
+
+/* Module definitions */
+
+int resumeline = 905;
+module_param(resumeline, int, 0444);
+
+static int noinit;
+module_param(noinit, int, 0444);
+
+/* ioctl() defines */
+
+#define DCONIOC_SOURCE _IOW('d', 0, int)
+#define DCONIOC_OUTPUT _IOW('d', 1, int)
+#define DCONIOC_SETREG _IOW('d', 2, int)
+#define DCONIOC_DUMPREG _IOW('d', 3, int)
+#define DCONIOC_GETREG _IOW('d', 4, int)
+#define DCONIOC_SETBL _IOW('d', 5, int)
+#define DCONIOC_GETBL _IOW('d', 6, int)
+
+/* I2C structures */
+
+static struct i2c_driver dcon_driver;
+static struct i2c_client *dcon_client;
+
+/* Base address of the GPIO registers */
+static unsigned long gpio_base;
+
+/* Current source */
+static int dcon_source = DCON_SOURCE_CPU;
+
+/* Current output type */
+static int dcon_output = DCON_OUTPUT_COLOR;
+
+/* Shadow register for the DCON_REG_MODE register */
+static unsigned short dcon_disp_mode;
+
+/* Variables used during switches */
+int dcon_waiting;
+
+static DECLARE_WAIT_QUEUE_HEAD(dcon_mode_wq);
+extern unsigned long gxfb_dc_regs;
+
+extern int gxfb_powerdown(struct fb_info *info);
+extern int gxfb_powerup(struct fb_info *info);
+
+static int dcon_set_source(struct fb_info *info, int arg)
+{
+ struct geodefb_par *par = info->par;
+ unsigned long val;
+
+ if (dcon_source == arg)
+ return 0;
+
+ dcon_source = arg;
+
+ if (arg == DCON_SOURCE_CPU) {
+ struct geodefb_par *par = info->par;
+ unsigned long timeo = jiffies + (10 * HZ);
+ unsigned long scanline1, scanline2;
+
+ /* Power up the graphics engine */
+ gxfb_powerup(info);
+
+ /* Wait for up to a second for our output to coincide with
+ DCON's */
+
+ 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;
+
+ if (!dconblnk && scanline1 >= resumeline &&
+ scanline1 <= (resumeline+2))
+ goto ready;
+
+#if 0
+ if (!dconblnk)
+ printk("Not ready. blnk %ld, line %ld\n",
+ dconblnk, scanline1);
+#endif
+ }
+
+ printk("Wait for VGA ready timed out\n");
+ready:
+ 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 {
+ int t;
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&dcon_mode_wq, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ dcon_waiting = 1;
+
+ /* 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);
+ set_current_state(TASK_RUNNING);
+
+ if (dcon_waiting) {
+ printk("Timeout entering DCON mode\n");
+
+ /* Re-enable GPIO11 - we never gave up 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);
+ }
+
+ i2c_smbus_write_word_data(dcon_client, DCON_REG_MODE, dcon_disp_mode);
+
+ return 0;
+}
+
+static int gxfb_dcon_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ int karg;
+ int ret = 0;
+ unsigned short reg, val;
+
+ switch (cmd) {
+ case DCONIOC_SOURCE:
+ if (get_user(karg, (int __user *)arg))
+ return -EFAULT;
+
+ if (karg > 1 || karg < -1)
+ return -EINVAL;
+
+ if (karg > -1)
+ ret = dcon_set_source(info, karg);
+
+ if (ret)
+ karg = -1;
+ else
+ karg = dcon_source;
+
+ ret = 0;
+ break;
+
+ case DCONIOC_OUTPUT:
+ if (get_user(karg, (int __user *)arg))
+ return -EFAULT;
+
+ if (karg > 1 || karg < -1)
+ return -EINVAL;
+
+ if (karg > -1)
+ ret = dcon_set_output(info, karg);
+
+ if (ret)
+ return ret;
+
+ karg = dcon_output;
+ break;
+
+ case DCONIOC_SETREG:
+ if (get_user(karg, (int __user *)arg))
+ return -EFAULT;
+ reg = karg >> 16;
+ val = karg & 0xffff;
+
+ if (reg >= 128)
+ return -EINVAL;
+
+ i2c_smbus_write_word_data(dcon_client, reg, val);
+ karg = 0;
+ break;
+
+ case DCONIOC_GETREG:
+ if (get_user(karg, (int __user *)arg))
+ return -EFAULT;
+
+ if (karg >= 128)
+ return -EINVAL;
+
+ karg = i2c_smbus_read_word_data(dcon_client, karg);
+ break;
+
+ case DCONIOC_DUMPREG:
+ for (reg = 0; reg < 11; reg++) {
+ val = i2c_smbus_read_word_data(dcon_client, reg);
+ printk("Reg %d: 0x%x\n", reg, val);
+ }
+ karg = 0;
+ break;
+
+ case DCONIOC_SETBL:
+ if (get_user(karg, (int __user *)arg))
+ return -EFAULT;
+
+ i2c_smbus_write_word_data(dcon_client, DCON_REG_BRIGHT,
+ karg & 0x0F);
+ karg = 0;
+ break;
+
+ case DCONIOC_GETBL:
+ if (get_user(karg, (int __user *)arg))
+ return -EFAULT;
+
+ val = i2c_smbus_read_word_data(dcon_client, DCON_REG_BRIGHT);
+ karg = val & 0x0F;
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+ if (put_user(karg, (int __user *)arg))
+ return -EFAULT;
+ return ret;
+}
+
+static unsigned short normal_i2c[] = { 0x0D, I2C_CLIENT_END };
+I2C_CLIENT_INSMOD;
+
+extern int (*gxfb_ioctl_func)(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+
+
+static int dcon_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct i2c_client *client;
+ uint16_t ver;
+ int rc;
+
+ 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");
+ kfree(client);
+ return rc;
+ }
+
+ 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;
+ }
+
+ 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 */
+
+ /* Switch to OLPC mode */
+ i2c_smbus_write_word_data(client, 0x0b, 0x002);
+
+ /* Colour swizzle, AA, no passthrough, backlight */
+
+ dcon_disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE |
+ MODE_CSWIZZLE | MODE_COL_AA;
+
+ i2c_smbus_write_word_data(client, DCON_REG_MODE,
+ dcon_disp_mode);
+
+ /* Initialise SDRAM */
+
+ i2c_smbus_write_word_data(client, 0x3a, 0xe040);
+ i2c_smbus_write_word_data(client, 0x3b, 0x0028);
+ i2c_smbus_write_word_data(client, 0x3c, 0x0000);
+ i2c_smbus_write_word_data(client, 0x3d, 0x0000);
+ i2c_smbus_write_word_data(client, 0x3e, 0x400f);
+ i2c_smbus_write_word_data(client, 0x3f, 0x04b0);
+ i2c_smbus_write_word_data(client, 0x40, 0x0384);
+ i2c_smbus_write_word_data(client, 0x41, 0x0101);
+ i2c_smbus_write_word_data(client, 0x42, 0x0101);
+ i2c_smbus_write_word_data(client, 0x43, 0x0101);
+ }
+
+ return 0;
+}
+
+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;
+ gxfb_ioctl_func = NULL;
+ dcon_client = NULL;
+
+ if ((rc = i2c_detach_client(client)) == 0)
+ kfree(i2c_get_clientdata(client));
+
+ return rc;
+}
+
+static struct i2c_driver dcon_driver = {
+ .driver = {
+ .name = "DCON",
+ },
+ .id = I2C_DRIVERID_DCON,
+ .attach_adapter = dcon_attach,
+ .detach_client = dcon_detach,
+};
+
+
+
+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;
+
+ /* 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);
+
+ 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);
+ }
+ return IRQ_HANDLED;
+}
+
+
+/* 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<<12))
+#define OUT_GPIOS (1<<11)
+
+int __init gxfb_dcon_init(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_WARNING "GPIO not enabled -- cannot use DCON\n");
+ return -ENODEV;
+ }
+
+ /* Mask off the IO base address */
+ gpio_base = lo & 0x0000ff00;
+
+ /* 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);
+
+ /* Turn off the event enable for GPIO7 just to be safe */
+ 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
+ */
+
+ 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 */
+ request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver);
+
+ /* 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);
+
+ /* Attach the I2C driver */
+ i2c_add_driver(&dcon_driver);
+
+ return 0;
+}
+
+void __exit gxfb_dcon_exit(void)
+{
+ free_irq(DCON_IRQ, &dcon_driver);
+ 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
new file mode 100644
index 0000000..31598e9
--- /dev/null
+++ b/drivers/video/geode/gxfb_dcon.h
@@ -0,0 +1,71 @@
+#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
+
+/* Interrupt */
+#define DCON_IRQ 6
+
+#endif
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c
index ed6a174..16d5b54 100644
--- a/drivers/video/geode/video_gx.c
+++ b/drivers/video/geode/video_gx.c
@@ -19,6 +19,7 @@ #include <asm/msr.h>
#include "geodefb.h"
#include "video_gx.h"
+#include "display_gx.h"
/*
@@ -289,6 +290,77 @@ static void gx_configure_display(struct
gx_configure_tft(info);
}
+/* This is a speedier way to power down the graphics engine -
+ it is intended to be called through the blanking infrastructure below
+ but also to be called by things like the DCON driver
+*/
+
+static int gxfb_powered_down;
+unsigned long gx_pm_regs[5];
+
+#define VC_VCFG 0
+#define VC_DCFG 1
+#define VC_FP_PM 2
+#define DC_GCFG 3
+#define DC_DCFG 4
+
+int gxfb_powerdown(struct fb_info *info) {
+
+ struct geodefb_par *par = info->par;
+
+ /* Bail if we have already saved our state and powered down */
+ if (gxfb_powered_down == 1)
+ return 0;
+
+ /* Disable the video hardware */
+ gx_pm_regs[VC_VCFG] = readl(par->vid_regs + GX_VCFG);
+ writel(gx_pm_regs[VC_VCFG] & ~0x01, par->vid_regs + GX_VCFG);
+
+ /* Black the dacs, turn off horiz / vert sync and turn off crt */
+ gx_pm_regs[VC_DCFG] = readl(par->vid_regs + GX_DCFG);
+ writel(gx_pm_regs[VC_DCFG] & ~0x0F, par->vid_regs + GX_DCFG);
+
+ /* Turn off the flat panel */
+ gx_pm_regs[VC_FP_PM] = readl(par->vid_regs + GX_FP_PM);
+ writel(gx_pm_regs[VC_FP_PM] & ~GX_FP_PM_P, par->vid_regs + GX_FP_PM);
+
+ /* Unlock the DC - this will remain unlocked until power up */
+ writel(0x4758, par->dc_regs + DC_UNLOCK);
+
+ /* Disable video, icon, cursor and the FIFO */
+ gx_pm_regs[DC_GCFG] = readl(par->dc_regs + DC_GENERAL_CFG);
+ writel(gx_pm_regs[DC_GCFG] & ~0x0F, par->dc_regs + DC_GENERAL_CFG);
+
+ /* Disable video data enable, graphics data enable and the timing generator */
+ gx_pm_regs[DC_DCFG] = readl(par->dc_regs + DC_DISPLAY_CFG);
+ writel(gx_pm_regs[DC_DCFG] & ~0x19, par->dc_regs + DC_DISPLAY_CFG);
+
+ gxfb_powered_down = 1;
+
+ return 0;
+}
+
+int gxfb_powerup(struct fb_info *info) {
+
+ struct geodefb_par *par = info->par;
+
+ if (gxfb_powered_down == 0)
+ return 0;
+
+ writel(gx_pm_regs[VC_VCFG], par->vid_regs + GX_VCFG);
+ writel(gx_pm_regs[VC_DCFG], par->vid_regs + GX_DCFG);
+ writel(gx_pm_regs[VC_FP_PM], par->vid_regs + GX_FP_PM);
+ writel(gx_pm_regs[DC_DCFG], par->dc_regs + DC_DISPLAY_CFG);
+
+ /* Do this one last because it will turn on the FIFO which will start the line count */
+ writel(gx_pm_regs[DC_GCFG], par->dc_regs + DC_GENERAL_CFG);
+
+ writel(0x0, par->dc_regs + DC_UNLOCK);
+
+ gxfb_powered_down = 0;
+ return 0;
+}
+
static int gx_blank_display(struct fb_info *info, int blank_mode)
{
struct geodefb_par *par = info->par;
@@ -315,6 +387,7 @@ static int gx_blank_display(struct fb_in
default:
return -EINVAL;
}
+
dcfg = readl(par->vid_regs + GX_DCFG);
dcfg &= ~(GX_DCFG_DAC_BL_EN
| GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN);
@@ -326,7 +399,7 @@ static int gx_blank_display(struct fb_in
dcfg |= GX_DCFG_VSYNC_EN;
writel(dcfg, par->vid_regs + GX_DCFG);
- /* Power on/off flat panel. */
+ /* Power on/off flat panel */
if (par->enable_crt == 0) {
fp_pm = readl(par->vid_regs + GX_FP_PM);
@@ -345,3 +418,9 @@ struct geode_vid_ops gx_vid_ops = {
.configure_display = gx_configure_display,
.blank_display = gx_blank_display,
};
+
+/* We define these as special hooks for the DCON driver to hook into */
+
+EXPORT_SYMBOL(gxfb_powerdown);
+EXPORT_SYMBOL(gxfb_powerup);
+
diff --git a/drivers/video/geode/video_gx.h b/drivers/video/geode/video_gx.h
index ce28d8f..4f9c9b3 100644
--- a/drivers/video/geode/video_gx.h
+++ b/drivers/video/geode/video_gx.h
@@ -20,6 +20,8 @@ #define GX_VP_PAD_SELECT_TFT 0
/* Geode GX video processor registers */
+#define GX_VCFG 0x0000
+
#define GX_DCFG 0x0008
# define GX_DCFG_CRT_EN 0x00000001
# define GX_DCFG_HSYNC_EN 0x00000002
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index 9418519..1d8aa7d 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -116,6 +116,7 @@ #define I2C_DRIVERID_BT866 85 /* Conexan
#define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */
#define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */
#define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */
+#define I2C_DRIVERID_DCON 89
#define I2C_DRIVERID_I2CDEV 900
#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
More information about the Commits-kernel
mailing list