Merge branch 'intelfb-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
Linus Torvalds [Sat, 30 Sep 2006 16:36:56 +0000 (09:36 -0700)]
* 'intelfb-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied/intelfb-2.6:
  intelfbhw.c: intelfbhw_get_p1p2 defined but not used
  intelfb: fix mtrr_reg signedness
  intelfb: update doc and Kconfig (supported devices)
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add preliminary i2c support
  intelfb: add vsync interrupt support
  intelfb: add vsync interrupt support
  intelfb: add vsync interrupt support
  intelfb: add vsync interrupt support
  intelfb: add vsync interrupt support

Documentation/fb/intelfb.txt
drivers/video/Kconfig
drivers/video/intelfb/Makefile
drivers/video/intelfb/intelfb.h
drivers/video/intelfb/intelfb_i2c.c [new file with mode: 0644]
drivers/video/intelfb/intelfbdrv.c
drivers/video/intelfb/intelfbhw.c
drivers/video/intelfb/intelfbhw.h
include/linux/i2c-id.h

index c12d39a..aa0d322 100644 (file)
@@ -1,16 +1,19 @@
-Intel 830M/845G/852GM/855GM/865G/915G Framebuffer driver
+Intel 830M/845G/852GM/855GM/865G/915G/945G Framebuffer driver
 ================================================================
 
 A. Introduction
-       This is a framebuffer driver for various Intel 810/815 compatible
+       This is a framebuffer driver for various Intel 8xx/9xx compatible
 graphics devices.  These would include:
 
        Intel 830M
-       Intel 810E845G
+       Intel 845G
        Intel 852GM
        Intel 855GM
        Intel 865G
        Intel 915G
+       Intel 915GM
+       Intel 945G
+       Intel 945GM
 
 B.  List of available options
 
@@ -78,7 +81,7 @@ C. Kernel booting
 Separate each option/option-pair by commas (,) and the option from its value
 with an equals sign (=) as in the following:
 
-video=i810fb:option1,option2=value2
+video=intelfb:option1,option2=value2
 
 Sample Usage
 ------------
index 702eb93..e0ef332 100644 (file)
@@ -825,30 +825,48 @@ config FB_I810_I2C
        help
 
 config FB_INTEL
-       tristate "Intel 830M/845G/852GM/855GM/865G support (EXPERIMENTAL)"
+       tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)"
        depends on FB && EXPERIMENTAL && PCI && X86
        select AGP
        select AGP_INTEL
+       select I2C_ALGOBIT if FB_INTEL_I2C
+       select I2C if FB_INTEL_I2C
        select FB_MODE_HELPERS
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
        help
          This driver supports the on-board graphics built in to the Intel
-          830M/845G/852GM/855GM/865G chipsets.
+          830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM chipsets.
           Say Y if you have and plan to use such a board.
 
-          To compile this driver as a module, choose M here: the
+         If you say Y here and want DDC/I2C support you must first say Y to
+         "I2C support" and "I2C bit-banging support" in the character devices
+         section.
+
+         If you say M here then "I2C support" and "I2C bit-banging support"
+         can be build either as modules or built-in.
+
+         To compile this driver as a module, choose M here: the
          module will be called intelfb.
 
+         For more information, please read <file:Documentation/fb/intelfb.txt>
+
 config FB_INTEL_DEBUG
-        bool "Intel driver Debug Messages"
+       bool "Intel driver Debug Messages"
        depends on FB_INTEL
        ---help---
          Say Y here if you want the Intel driver to output all sorts
          of debugging informations to provide to the maintainer when
          something goes wrong.
 
+config FB_INTEL_I2C
+       bool "DDC/I2C for Intel framebuffer support"
+       depends on FB_INTEL
+       default y
+       help
+         Say Y here if you want DDC/I2C support for your on-board Intel graphics.
+
 config FB_MATROX
        tristate "Matrox acceleration"
        depends on FB && PCI
index 722d21d..6c782d3 100644 (file)
@@ -1,6 +1,8 @@
 obj-$(CONFIG_FB_INTEL) += intelfb.o
 
-intelfb-objs := intelfbdrv.o intelfbhw.o
+intelfb-y := intelfbdrv.o intelfbhw.o
+intelfb-$(CONFIG_FB_INTEL_I2C) += intelfb_i2c.o
+intelfb-objs := $(intelfb-y)
 
 ifdef CONFIG_FB_INTEL_DEBUG
 #EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP
index e290d74..80b94c1 100644 (file)
@@ -6,6 +6,10 @@
 #include <linux/agp_backend.h>
 #include <linux/fb.h>
 
+#ifdef CONFIG_FB_INTEL_I2C
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#endif
 
 /*** Version/name ***/
 #define INTELFB_VERSION                        "0.9.4"
 /* Intel agpgart driver */
 #define AGP_PHYSICAL_MEMORY     2
 
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+
+/* these are outputs from the chip - integrated only
+   external chips are via DVO or SDVO output */
+#define INTELFB_OUTPUT_UNUSED 0
+#define INTELFB_OUTPUT_ANALOG 1
+#define INTELFB_OUTPUT_DVO 2
+#define INTELFB_OUTPUT_SDVO 3
+#define INTELFB_OUTPUT_LVDS 4
+#define INTELFB_OUTPUT_TVOUT 5
+
+#define INTELFB_DVO_CHIP_NONE 0
+#define INTELFB_DVO_CHIP_LVDS 1
+#define INTELFB_DVO_CHIP_TMDS 2
+#define INTELFB_DVO_CHIP_TVOUT 4
+
+#define INTELFB_OUTPUT_PIPE_NC  0
+#define INTELFB_OUTPUT_PIPE_A   1
+#define INTELFB_OUTPUT_PIPE_B   2
+
 /*** Data Types ***/
 
 /* supported chipsets */
@@ -195,6 +222,10 @@ struct intelfb_hwstate {
        u32 mem_mode;
        u32 fw_blc_0;
        u32 fw_blc_1;
+       u16 hwstam;
+       u16 ier;
+       u16 iir;
+       u16 imr;
 };
 
 struct intelfb_heap_data {
@@ -204,6 +235,33 @@ struct intelfb_heap_data {
        u32 size;    // in bytes
 };
 
+#ifdef CONFIG_FB_INTEL_I2C
+struct intelfb_i2c_chan {
+    struct intelfb_info *dinfo;
+    u32 reg;
+    struct i2c_adapter adapter;
+    struct i2c_algo_bit_data algo;
+};
+#endif
+
+struct intelfb_output_rec {
+    int type;
+    int pipe;
+    int flags;
+
+#ifdef CONFIG_FB_INTEL_I2C
+    struct intelfb_i2c_chan i2c_bus;
+    struct intelfb_i2c_chan ddc_bus;
+#endif
+};
+
+struct intelfb_vsync {
+       wait_queue_head_t wait;
+       unsigned int count;
+       int pan_display;
+       u32 pan_offset;
+};
+
 struct intelfb_info {
        struct fb_info *info;
        struct fb_ops  *fbops;
@@ -220,7 +278,7 @@ struct intelfb_info {
        u8 fbmem_gart;
 
        /* mtrr support */
-       u32 mtrr_reg;
+       int mtrr_reg;
        u32 has_mtrr;
 
        /* heap data */
@@ -267,6 +325,12 @@ struct intelfb_info {
        int fixed_mode;
        int ring_active;
        int flag;
+       unsigned long irq_flags;
+       int open;
+
+       /* vsync */
+       struct intelfb_vsync vsync;
+       spinlock_t int_lock;
 
        /* hw cursor */
        int cursor_on;
@@ -285,12 +349,25 @@ struct intelfb_info {
        
        /* index into plls */
        int pll_index;
+
+       /* outputs */
+       int num_outputs;
+       struct intelfb_output_rec output[MAX_OUTPUTS];
 };
 
 #define IS_I9XX(dinfo) (((dinfo)->chipset == INTEL_915G)||(dinfo->chipset == INTEL_915GM)||((dinfo)->chipset == INTEL_945G)||(dinfo->chipset==INTEL_945GM))
 
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC      _IOW('F', 0x20, __u32)
+#endif
+
 /*** function prototypes ***/
 
 extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var);
 
+#ifdef CONFIG_FB_INTEL_I2C
+extern void intelfb_create_i2c_busses(struct intelfb_info *dinfo);
+extern void intelfb_delete_i2c_busses(struct intelfb_info *dinfo);
+#endif
+
 #endif /* _INTELFB_H */
diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c
new file mode 100644 (file)
index 0000000..c1113d6
--- /dev/null
@@ -0,0 +1,200 @@
+/**************************************************************************
+
+ Copyright 2006 Dave Airlie <airlied@linux.ie>
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+on the rights to use, copy, modify, merge, publish, distribute, sub
+license, and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/io.h>
+
+#include "intelfb.h"
+#include "intelfbhw.h"
+
+/* bit locations in the registers */
+#define SCL_DIR_MASK           0x0001
+#define SCL_DIR                        0x0002
+#define SCL_VAL_MASK           0x0004
+#define SCL_VAL_OUT            0x0008
+#define SCL_VAL_IN             0x0010
+#define SDA_DIR_MASK           0x0100
+#define SDA_DIR                        0x0200
+#define SDA_VAL_MASK           0x0400
+#define SDA_VAL_OUT            0x0800
+#define SDA_VAL_IN             0x1000
+
+static void intelfb_gpio_setscl(void *data, int state)
+{
+       struct intelfb_i2c_chan *chan = data;
+       struct intelfb_info *dinfo = chan->dinfo;
+       u32 val;
+
+       OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK);
+       val = INREG(chan->reg);
+}
+
+static void intelfb_gpio_setsda(void *data, int state)
+{
+       struct intelfb_i2c_chan *chan = data;
+       struct intelfb_info *dinfo = chan->dinfo;
+       u32 val;
+
+       OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK);
+       val = INREG(chan->reg);
+}
+
+static int intelfb_gpio_getscl(void *data)
+{
+       struct intelfb_i2c_chan *chan = data;
+       struct intelfb_info *dinfo = chan->dinfo;
+       u32 val;
+
+       OUTREG(chan->reg, SCL_DIR_MASK);
+       OUTREG(chan->reg, 0);
+       val = INREG(chan->reg);
+       return ((val & SCL_VAL_IN) != 0);
+}
+
+static int intelfb_gpio_getsda(void *data)
+{
+       struct intelfb_i2c_chan *chan = data;
+       struct intelfb_info *dinfo = chan->dinfo;
+       u32 val;
+
+       OUTREG(chan->reg, SDA_DIR_MASK);
+       OUTREG(chan->reg, 0);
+       val = INREG(chan->reg);
+       return ((val & SDA_VAL_IN) != 0);
+}
+
+static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo,
+                                                                struct intelfb_i2c_chan *chan,
+                                                                const u32 reg, const char *name)
+{
+       int rc;
+
+       chan->dinfo                                     = dinfo;
+       chan->reg                                       = reg;
+       snprintf(chan->adapter.name, I2C_NAME_SIZE, "intelfb %s", name);
+       chan->adapter.owner                     = THIS_MODULE;
+       chan->adapter.id                        = I2C_HW_B_INTELFB;
+       chan->adapter.algo_data         = &chan->algo;
+       chan->adapter.dev.parent        = &chan->dinfo->pdev->dev;
+       chan->algo.setsda                       = intelfb_gpio_setsda;
+       chan->algo.setscl                       = intelfb_gpio_setscl;
+       chan->algo.getsda                       = intelfb_gpio_getsda;
+       chan->algo.getscl                       = intelfb_gpio_getscl;
+       chan->algo.udelay                       = 40;
+       chan->algo.timeout                      = 20;
+       chan->algo.data                         = chan;
+
+       i2c_set_adapdata(&chan->adapter, chan);
+
+       /* Raise SCL and SDA */
+       intelfb_gpio_setsda(chan, 1);
+       intelfb_gpio_setscl(chan, 1);
+       udelay(20);
+
+       rc = i2c_bit_add_bus(&chan->adapter);
+       if (rc == 0)
+               DBG_MSG("I2C bus %s registered.\n", name);
+       else
+               WRN_MSG("Failed to register I2C bus %s.\n", name);
+       return rc;
+}
+
+void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
+{
+       int i = 0;
+
+       /* everyone has at least a single analog output */
+       dinfo->num_outputs = 1;
+       dinfo->output[i].type = INTELFB_OUTPUT_ANALOG;
+
+       /* setup the DDC bus for analog output */
+       intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, "CRTDDC_A");
+       i++;
+
+    /* need to add the output busses for each device
+       - this function is very incomplete
+       - i915GM has LVDS and TVOUT for example
+    */
+    switch(dinfo->chipset) {
+       case INTEL_830M:
+       case INTEL_845G:
+       case INTEL_855GM:
+       case INTEL_865G:
+               dinfo->output[i].type = INTELFB_OUTPUT_DVO;
+               intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOD, "DVODDC_D");
+               intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "DVOI2C_E");
+               i++;
+               break;
+       case INTEL_915G:
+       case INTEL_915GM:
+               /* has  some LVDS + tv-out */
+       case INTEL_945G:
+       case INTEL_945GM:
+               /* SDVO ports have a single control bus - 2 devices */
+               dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
+               intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "SDVOCTRL_E");
+               /* TODO: initialize the SDVO */
+//             I830SDVOInit(pScrn, i, DVOB);
+               i++;
+
+               /* set up SDVOC */
+               dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
+               dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus;
+               /* TODO: initialize the SDVO */
+//             I830SDVOInit(pScrn, i, DVOC);
+               i++;
+               break;
+       }
+       dinfo->num_outputs = i;
+}
+
+void intelfb_delete_i2c_busses(struct intelfb_info *dinfo)
+{
+       int i;
+
+       for (i = 0; i < MAX_OUTPUTS; i++) {
+               if (dinfo->output[i].i2c_bus.dinfo) {
+                       i2c_bit_del_bus(&dinfo->output[i].i2c_bus.adapter);
+                       dinfo->output[i].i2c_bus.dinfo = NULL;
+               }
+               if (dinfo->output[i].ddc_bus.dinfo) {
+                       i2c_bit_del_bus(&dinfo->output[i].ddc_bus.adapter);
+                       dinfo->output[i].ddc_bus.dinfo = NULL;
+               }
+       }
+}
index 06af89d..6f9de04 100644 (file)
 static void __devinit get_initial_mode(struct intelfb_info *dinfo);
 static void update_dinfo(struct intelfb_info *dinfo,
                         struct fb_var_screeninfo *var);
+static int intelfb_open(struct fb_info *info, int user);
+static int intelfb_release(struct fb_info *info, int user);
 static int intelfb_check_var(struct fb_var_screeninfo *var,
                             struct fb_info *info);
 static int intelfb_set_par(struct fb_info *info);
@@ -194,6 +196,8 @@ static int num_registered = 0;
 /* fb ops */
 static struct fb_ops intel_fb_ops = {
        .owner =                THIS_MODULE,
+       .fb_open =              intelfb_open,
+       .fb_release =           intelfb_release,
        .fb_check_var =         intelfb_check_var,
        .fb_set_par =           intelfb_set_par,
        .fb_setcolreg =         intelfb_setcolreg,
@@ -446,6 +450,8 @@ cleanup(struct intelfb_info *dinfo)
        if (!dinfo)
                return;
 
+       intelfbhw_disable_irq(dinfo);
+
        fb_dealloc_cmap(&dinfo->info->cmap);
        kfree(dinfo->info->pixmap.addr);
 
@@ -467,6 +473,11 @@ cleanup(struct intelfb_info *dinfo)
                agp_free_memory(dinfo->gtt_ring_mem);
        }
 
+#ifdef CONFIG_FB_INTEL_I2C
+       /* un-register I2C bus */
+       intelfb_delete_i2c_busses(dinfo);
+#endif
+
        if (dinfo->mmio_base)
                iounmap((void __iomem *)dinfo->mmio_base);
        if (dinfo->aperture.virtual)
@@ -844,6 +855,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (bailearly == 5)
                bailout(dinfo);
 
+#ifdef CONFIG_FB_INTEL_I2C
+       /* register I2C bus */
+       intelfb_create_i2c_busses(dinfo);
+#endif
+
        if (bailearly == 6)
                bailout(dinfo);
 
@@ -888,6 +904,13 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        dinfo->registered = 1;
+       dinfo->open = 0;
+
+       init_waitqueue_head(&dinfo->vsync.wait);
+       spin_lock_init(&dinfo->int_lock);
+       dinfo->irq_flags = 0;
+       dinfo->vsync.pan_display = 0;
+       dinfo->vsync.pan_offset = 0;
 
        return 0;
 
@@ -1188,6 +1211,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
  ***************************************************************/
 
 static int
+intelfb_open(struct fb_info *info, int user)
+{
+       struct intelfb_info *dinfo = GET_DINFO(info);
+
+       if (user) {
+               dinfo->open++;
+       }
+
+       return 0;
+}
+
+static int
+intelfb_release(struct fb_info *info, int user)
+{
+       struct intelfb_info *dinfo = GET_DINFO(info);
+
+       if (user) {
+               dinfo->open--;
+               msleep(1);
+               if (!dinfo->open) {
+                       intelfbhw_disable_irq(dinfo);
+               }
+       }
+
+       return 0;
+}
+
+static int
 intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
        int change_var = 0;
@@ -1433,6 +1484,19 @@ static int
 intelfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 {
        int retval = 0;
+       struct intelfb_info *dinfo = GET_DINFO(info);
+       u32 pipe = 0;
+
+       switch (cmd) {
+               case FBIO_WAITFORVSYNC:
+                       if (get_user(pipe, (__u32 __user *)arg))
+                               return -EFAULT;
+
+                       retval = intelfbhw_wait_for_vsync(dinfo, pipe);
+                       break;
+               default:
+                       break;
+       }
 
        return retval;
 }
index 2a9322f..f887f1e 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
+#include <linux/interrupt.h>
 
 #include <asm/io.h>
 
@@ -368,7 +369,13 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
 
        offset += dinfo->fb.offset << 12;
 
-       OUTREG(DSPABASE, offset);
+       dinfo->vsync.pan_offset = offset;
+       if ((var->activate & FB_ACTIVATE_VBL) && !intelfbhw_enable_irq(dinfo, 0)) {
+               dinfo->vsync.pan_display = 1;
+       } else {
+               dinfo->vsync.pan_display = 0;
+               OUTREG(DSPABASE, offset);
+       }
 
        return 0;
 }
@@ -585,6 +592,11 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
        hw->fw_blc_0 = INREG(FW_BLC_0);
        hw->fw_blc_1 = INREG(FW_BLC_1);
 
+       hw->hwstam = INREG16(HWSTAM);
+       hw->ier = INREG16(IER);
+       hw->iir = INREG16(IIR);
+       hw->imr = INREG16(IMR);
+
        return 0;
 }
 
@@ -613,6 +625,7 @@ static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, int lvd
        return vco / p;
 }
 
+#if REGDUMP
 static void
 intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
 {
@@ -638,6 +651,7 @@ intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
        *o_p1 = p1;
        *o_p2 = p2;
 }
+#endif
 
 
 void
@@ -794,6 +808,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw)
        printk("        FW_BLC_0                0x%08x\n", hw->fw_blc_0);
        printk("        FW_BLC_1                0x%08x\n", hw->fw_blc_1);
 
+       printk("        HWSTAM                  0x%04x\n", hw->hwstam);
+       printk("        IER                     0x%04x\n", hw->ier);
+       printk("        IIR                     0x%04x\n", hw->iir);
+       printk("        IMR                     0x%04x\n", hw->imr);
        printk("hw state dump end\n");
 #endif
 }
@@ -1932,3 +1950,119 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
                addr += 16;
        }
 }
+
+static irqreturn_t
+intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) {
+       int handled = 0;
+       u16 tmp;
+       struct intelfb_info *dinfo = (struct intelfb_info *)dev_id;
+
+       spin_lock(&dinfo->int_lock);
+
+       tmp = INREG16(IIR);
+       tmp &= VSYNC_PIPE_A_INTERRUPT;
+
+       if (tmp == 0) {
+               spin_unlock(&dinfo->int_lock);
+               return IRQ_RETVAL(handled);
+       }
+
+       OUTREG16(IIR, tmp);
+
+       if (tmp & VSYNC_PIPE_A_INTERRUPT) {
+               dinfo->vsync.count++;
+               if (dinfo->vsync.pan_display) {
+                       dinfo->vsync.pan_display = 0;
+                       OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+               }
+               wake_up_interruptible(&dinfo->vsync.wait);
+               handled = 1;
+       }
+
+       spin_unlock(&dinfo->int_lock);
+
+       return IRQ_RETVAL(handled);
+}
+
+int
+intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) {
+
+       if (!test_and_set_bit(0, &dinfo->irq_flags)) {
+               if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) {
+                       clear_bit(0, &dinfo->irq_flags);
+                       return -EINVAL;
+               }
+
+               spin_lock_irq(&dinfo->int_lock);
+               OUTREG16(HWSTAM, 0xfffe);
+               OUTREG16(IMR, 0x0);
+               OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT);
+               spin_unlock_irq(&dinfo->int_lock);
+       } else if (reenable) {
+               u16 ier;
+
+               spin_lock_irq(&dinfo->int_lock);
+               ier = INREG16(IER);
+               if ((ier & VSYNC_PIPE_A_INTERRUPT)) {
+                       DBG_MSG("someone disabled the IRQ [%08X]\n", ier);
+                       OUTREG(IER, VSYNC_PIPE_A_INTERRUPT);
+               }
+               spin_unlock_irq(&dinfo->int_lock);
+       }
+       return 0;
+}
+
+void
+intelfbhw_disable_irq(struct intelfb_info *dinfo) {
+       u16 tmp;
+
+       if (test_and_clear_bit(0, &dinfo->irq_flags)) {
+               if (dinfo->vsync.pan_display) {
+                       dinfo->vsync.pan_display = 0;
+                       OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+               }
+               spin_lock_irq(&dinfo->int_lock);
+               OUTREG16(HWSTAM, 0xffff);
+               OUTREG16(IMR, 0xffff);
+               OUTREG16(IER, 0x0);
+
+               tmp = INREG16(IIR);
+               OUTREG16(IIR, tmp);
+               spin_unlock_irq(&dinfo->int_lock);
+
+               free_irq(dinfo->pdev->irq, dinfo);
+       }
+}
+
+int
+intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) {
+       struct intelfb_vsync *vsync;
+       unsigned int count;
+       int ret;
+
+       switch (pipe) {
+               case 0:
+                       vsync = &dinfo->vsync;
+                       break;
+               default:
+                       return -ENODEV;
+       }
+
+       ret = intelfbhw_enable_irq(dinfo, 0);
+       if (ret) {
+               return ret;
+       }
+
+       count = vsync->count;
+       ret = wait_event_interruptible_timeout(vsync->wait, count != vsync->count, HZ/10);
+       if (ret < 0) {
+               return ret;
+       }
+       if (ret == 0) {
+               intelfbhw_enable_irq(dinfo, 1);
+               DBG_MSG("wait_for_vsync timed out!\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
index 10acda0..8c54ba8 100644 (file)
 #define INSTDONE               0x2090
 #define PRI_RING_EMPTY                 1
 
+#define HWSTAM                 0x2098
+#define IER                    0x20A0
+#define IIR                    0x20A4
+#define IMR                    0x20A8
+#define VSYNC_PIPE_A_INTERRUPT         (1 << 7)
+#define PIPE_A_EVENT_INTERRUPT         (1 << 4)
+#define VSYNC_PIPE_B_INTERRUPT         (1 << 5)
+#define PIPE_B_EVENT_INTERRUPT         (1 << 4)
+#define HOST_PORT_EVENT_INTERRUPT      (1 << 3)
+#define CAPTURE_EVENT_INTERRUPT                (1 << 2)
+#define USER_DEFINED_INTERRUPT         (1 << 1)
+#define BREAKPOINT_INTERRUPT           1
+
 #define INSTPM                 0x20c0
 #define SYNC_FLUSH_ENABLE              (1 << 5)
 
 #define FW_DISPC_BL_SHIFT              8
 #define FW_DISPC_BL_MASK               0x7
 
+#define GPIOA             0x5010
+#define GPIOB             0x5014
+#define GPIOC             0x5018 // this may be external DDC on i830
+#define GPIOD             0x501C // this is DVO DDC
+#define GPIOE             0x5020 // this is DVO i2C
+#define GPIOF             0x5024
 
 /* PLL registers */
 #define VGA0_DIVISOR           0x06000
 
 /* I/O macros */
 #define INREG8(addr)         readb((u8 __iomem *)(dinfo->mmio_base + (addr)))
+#define INREG16(addr)        readw((u16 __iomem *)(dinfo->mmio_base + (addr)))
 #define INREG(addr)          readl((u32 __iomem *)(dinfo->mmio_base + (addr)))
 #define OUTREG8(addr, val)    writeb((val),(u8 __iomem *)(dinfo->mmio_base + \
                                                           (addr)))
+#define OUTREG16(addr, val)    writew((val),(u16 __iomem *)(dinfo->mmio_base + \
+                                                          (addr)))
 #define OUTREG(addr, val)     writel((val),(u32 __iomem *)(dinfo->mmio_base + \
                                      (addr)))
 
@@ -545,5 +567,8 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg,
 extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width,
                                  int height, u8 *data);
 extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
+extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable);
+extern void intelfbhw_disable_irq(struct intelfb_info *dinfo);
+extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe);
 
 #endif /* _INTELFBHW_H */
index 9418519..0a8f750 100644 (file)
 #define I2C_HW_B_RADEON                0x01001e /* radeon framebuffer driver */
 #define I2C_HW_B_EM28XX                0x01001f /* em28xx video capture cards */
 #define I2C_HW_B_CX2341X       0x010020 /* Conexant CX2341X MPEG encoder cards */
+#define I2C_HW_B_INTELFB       0x010021 /* intel framebuffer driver */
 
 /* --- PCF 8584 based algorithms                                       */
 #define I2C_HW_P_LP            0x020000 /* Parallel port interface */