vserver 1.9.5.x5
[linux-2.6.git] / drivers / media / video / vino.c
index 25f4f2f..76e8681 100644 (file)
-/* $Id: vino.c,v 1.5 1999/10/09 00:01:14 ralf Exp $
- * drivers/char/vino.c
+/*
+ * (incomplete) Driver for the VINO (Video In No Out) system found in SGI Indys.
  *
- * (incomplete) Driver for the Vino Video input system found in SGI Indys.
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
  *
- * Copyright (C) 1999 Ulf Carlsson (ulfc@bun.falkenberg.se)
- *
- * This isn't complete yet, please don't expect any video until I've written
- * some more code.
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
  */
 
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/wrapper.h>
 #include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
 #include <linux/videodev.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-sgi.h>
 
 #include <asm/addrspace.h>
 #include <asm/system.h>
+#include <asm/bootinfo.h>
+#include <asm/pgtable.h>
+#include <asm/paccess.h>
+#include <asm/io.h>
+#include <asm/sgi/ip22.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
 
 #include "vino.h"
 
+/* debugging? */
+#if 1
+#define DEBUG(x...)     printk(x);
+#else
+#define DEBUG(x...)
+#endif
+
+
+/* VINO ASIC registers */
+struct sgi_vino *vino;
+
+static const char *vinostr = "VINO IndyCam/TV";
+static int threshold_a = 512;
+static int threshold_b = 512;
+
 struct vino_device {
        struct video_device vdev;
+#define VINO_CHAN_A            1
+#define VINO_CHAN_B            2
+       int chan;
+};
 
-       unsigned long chan;
-#define VINO_CHAN_A            0
-#define VINO_CHAN_B            1
-
-       unsigned long flags;
-#define VINO_DMA_ACTIVE                (1<<0)
+struct vino_client {
+       struct i2c_client *driver;
+       int owner;
 };
 
-/* We can actually receive TV and IndyCam input at the same time. Believe it or
- * not..
- */
-static struct vino_device vino[2];
+struct vino_video {
+       struct vino_device chA;
+       struct vino_device chB;
 
-/* Those registers have to be accessed by either *one* 64 bit write or *one* 64
- * bit read. We need some asm to fix this. We can't use mips3 as standard
- * because we just save 32 bits at context switch.
- */
+       struct vino_client decoder;
+       struct vino_client camera;
 
-static __inline__ unsigned long long vino_reg_read(unsigned long addr)
-{
-       unsigned long long ret __attribute__ ((aligned (64)));
-       unsigned long virt_addr = KSEG1ADDR(addr + VINO_BASE);
-       unsigned long flags;
-
-       save_and_cli(flags);
-       __asm__ __volatile__(
-               ".set\tmips3\n\t"
-               ".set\tnoat\n\t"
-               "ld\t$1,(%0)\n\t"
-               "sd\t$1,(%1)\n\t"
-               ".set\tat\n\t"
-               ".set\tmips0"
-               :
-               :"r" (virt_addr),
-                "r" (&ret)
-               :"$1");
-       restore_flags(flags);
+       struct semaphore input_lock;
 
-       return ret;
+       /* Loaded into VINO descriptors to clear End Of Descriptors table
+        * interupt condition */
+       unsigned long dummy_page;
+       unsigned int dummy_buf[4] __attribute__((aligned(8)));
+};
+
+static struct vino_video *Vino;
+
+unsigned i2c_vino_getctrl(void *data)
+{
+       return vino->i2c_control;
 }
 
-static __inline__ void vino_reg_write(unsigned long long value,
-                                     unsigned long addr)
+void i2c_vino_setctrl(void *data, unsigned val)
 {
-       unsigned long virt_addr = KSEG1ADDR(addr + VINO_BASE);
-       unsigned long flags;
-
-       /* we might lose the upper parts of the registers which are not saved
-        * if there comes an interrupt in our way, play safe */
-
-       save_and_cli(flags);
-       __asm__ __volatile__(
-               ".set\tmips3\n\t"
-               ".set\tnoat\n\t"
-               "ld\t$1,(%0)\n\t"
-               "sd\t$1,(%1)\n\t"
-               ".set\tat\n\t"
-               ".set\tmips0"
-               :
-               :"r" (&value),
-                "r" (virt_addr)
-               :"$1");
-       restore_flags(flags);
+       vino->i2c_control = val;
 }
 
-static __inline__ void vino_reg_and(unsigned long long value,
-                                   unsigned long addr)
+unsigned i2c_vino_rdata(void *data)
 {
-       unsigned long virt_addr = KSEG1ADDR(addr + VINO_BASE);
-       unsigned long flags;
-
-       save_and_cli(flags);
-       __asm__ __volatile__(
-               ".set\tmips3\n\t"
-               ".set\tnoat\n\t"
-               "ld\t$1,(%0)\n\t"
-               "ld\t$2,(%1)\n\t"
-               "and\t$1,$1,$2\n\t"
-               "sd\t$1,(%0)\n\t"
-               ".set\tat\n\t"
-               ".set\tmips0"
-               :
-               :"r" (virt_addr),
-                "r" (&value)
-               :"$1","$2");
-       restore_flags(flags);
+       return vino->i2c_data;
 }
 
-static __inline__ void vino_reg_or(unsigned long long value,
-                                  unsigned long addr)
+void i2c_vino_wdata(void *data, unsigned val)
 {
-       unsigned long virt_addr = KSEG1ADDR(addr + VINO_BASE);
-       unsigned long flags;
-
-       save_and_cli(flags);
-       __asm__ __volatile__(
-               ".set\tmips3\n\t"
-               ".set\tnoat\n\t"
-               "ld\t$1,(%0)\n\t"
-               "ld\t$2,(%1)\n\t"
-               "or\t$1,$1,$2\n\t"
-               "sd\t$1,(%0)\n\t"
-               ".set\tat\n\t"
-               ".set\tmips0"
-               :
-               :"r" (virt_addr),
-                "r" (&value)
-               :"$1","$2");
-       restore_flags(flags);
+       vino->i2c_data = val;
 }
 
-static int vino_dma_setup(void)
+static struct i2c_algo_sgi_data i2c_sgi_vino_data =
 {
-       return 0;
+       .getctrl = &i2c_vino_getctrl,
+       .setctrl = &i2c_vino_setctrl,
+       .rdata   = &i2c_vino_rdata,
+       .wdata   = &i2c_vino_wdata,
+       .xfer_timeout = 200,
+       .ack_timeout  = 1000,
+};
+
+/*
+ * There are two possible clients on VINO I2C bus, so we limit usage only
+ * to them.
+ */
+static int i2c_vino_client_reg(struct i2c_client *client)
+{
+       int res = 0;
+
+       down(&Vino->input_lock);
+       switch (client->driver->id) {
+       case I2C_DRIVERID_SAA7191:
+               if (Vino->decoder.driver)
+                       res = -EBUSY;
+               else
+                       Vino->decoder.driver = client;
+               break;
+       case I2C_DRIVERID_INDYCAM:
+               if (Vino->camera.driver)
+                       res = -EBUSY;
+               else
+                       Vino->camera.driver = client;
+               break;
+       default:
+               res = -ENODEV;
+       }
+       up(&Vino->input_lock);
+
+       return res;
 }
 
-static void vino_dma_stop(void)
+static int i2c_vino_client_unreg(struct i2c_client *client)
 {
+       int res = 0;
+
+       down(&Vino->input_lock);
+       if (client == Vino->decoder.driver) {
+               if (Vino->decoder.owner)
+                       res = -EBUSY;
+               else
+                       Vino->decoder.driver = NULL;
+       } else if (client == Vino->camera.driver) {
+               if (Vino->camera.owner)
+                       res = -EBUSY;
+               else
+                       Vino->camera.driver = NULL;
+       }
+       up(&Vino->input_lock);
 
+       return res;
 }
 
-static int vino_init(void)
+static struct i2c_adapter vino_i2c_adapter =
 {
-       unsigned long ret;
-       unsigned short rev, id;
-       unsigned long long foo;
-       unsigned long *bar;
-
-       bar = (unsigned long *) &foo;
-
-       ret = vino_reg_read(VINO_REVID);
-
-       rev = (ret & VINO_REVID_REV_MASK);
-       id = (ret & VINO_REVID_ID_MASK) >> 4;
-
-       printk("Vino: ID:%02hx Rev:%02hx\n", id, rev);
-
-       foo = vino_reg_read(VINO_A_DESC_DATA0);
-       printk("0x%lx", bar[0]);
-       printk("%lx ", bar[1]);
-       foo = vino_reg_read(VINO_A_DESC_DATA1);
-       printk("0x%lx", bar[0]);
-       printk("%lx ", bar[1]);
-       foo = vino_reg_read(VINO_A_DESC_DATA2);
-       printk("0x%lx", bar[0]);
-       printk("%lx ", bar[1]);
-       foo = vino_reg_read(VINO_A_DESC_DATA3);
-       printk("0x%lx", bar[0]);
-       printk("%lx\n", bar[1]);
-       foo = vino_reg_read(VINO_B_DESC_DATA0);
-       printk("0x%lx", bar[0]);
-       printk("%lx ", bar[1]);
-       foo = vino_reg_read(VINO_B_DESC_DATA1);
-       printk("0x%lx", bar[0]);
-       printk("%lx ", bar[1]);
-       foo = vino_reg_read(VINO_B_DESC_DATA2);
-       printk("0x%lx", bar[0]);
-       printk("%lx ", bar[1]);
-       foo = vino_reg_read(VINO_B_DESC_DATA3);
-       printk("0x%lx", bar[0]);
-       printk("%lx\n", bar[1]);
+       .name                   = "VINO I2C bus",
+       .id                     = I2C_HW_SGI_VINO,
+       .algo_data              = &i2c_sgi_vino_data,
+       .client_register        = &i2c_vino_client_reg,
+       .client_unregister      = &i2c_vino_client_unreg,
+};
 
-       return 0;
+static int vino_i2c_add_bus(void)
+{
+       return i2c_sgi_add_bus(&vino_i2c_adapter);
 }
 
-static void vino_dma_go(struct vino_device *v)
+static int vino_i2c_del_bus(void)
 {
-       
+       return i2c_sgi_del_bus(&vino_i2c_adapter);
 }
 
-/* Reset the vino back to default state */
 
-static void vino_setup(struct vino_device *v)
+static void vino_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       
 }
 
 static int vino_open(struct video_device *dev, int flags)
 {
+       struct vino_device *videv = (struct vino_device *)dev;
+
        return 0;
 }
 
 static void vino_close(struct video_device *dev)
 {
+       struct vino_device *videv = (struct vino_device *)dev;
 }
 
-static int vino_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int vino_mmap(struct video_device *dev, const char *adr,
+                    unsigned long size)
 {
-       return 0;
+       struct vino_device *videv = (struct vino_device *)dev;
+
+       return -EINVAL;
 }
 
-static int vino_mmap(struct video_device *dev, const char *adr,
-                    unsigned long size)
+static int vino_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
 {
-       return 0;
+       struct vino_device *videv = (struct vino_device *)dev;
+
+       return -EINVAL;
 }
 
-static struct video_device vino_dev = {
+static const struct video_device vino_device = {
        .owner          = THIS_MODULE,
-       .name           = "Vino IndyCam/TV",
-       .type           = VID_TYPE_CAPTURE,
+       .type           = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE,
        .hardware       = VID_HARDWARE_VINO,
+       .name           = "VINO",
        .open           = vino_open,
        .close          = vino_close,
        .ioctl          = vino_ioctl,
        .mmap           = vino_mmap,
 };
 
-int __init init_vino(struct video_device *dev)
+static int __init vino_init(void)
 {
-       int err;
+       unsigned long rev;
+       int i, ret = 0;
 
-       err = vino_init();
-       if (err)
-               return err;
+       /* VINO is Indy specific beast */
+       if (ip22_is_fullhouse())
+               return -ENODEV;
 
-#if 0
-       if (video_register_device(&vinodev, VFL_TYPE_GRABBER) == -1) {
+       /*
+        * VINO is in the EISA address space, so the sysid register will tell
+        * us if the EISA_PRESENT pin on MC has been pulled low.
+        *
+        * If EISA_PRESENT is not set we definitely don't have a VINO equiped
+        * system.
+        */
+       if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+               printk(KERN_ERR "VINO not found\n");
                return -ENODEV;
        }
-#endif
 
-       return 0;
-}
+       vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
+       if (!vino)
+               return -EIO;
 
-#ifdef MODULE
-int init_module(void)
-{
-       int err;
+       /* Okay, once we know that VINO is present we'll read its revision
+        * safe way. One never knows... */
+       if (get_dbe(rev, &(vino->rev_id))) {
+               printk(KERN_ERR "VINO: failed to read revision register\n");
+               ret = -ENODEV;
+               goto out_unmap;
+       }
+       if (VINO_ID_VALUE(rev) != VINO_CHIP_ID) {
+               printk(KERN_ERR "VINO is not VINO (Rev/ID: 0x%04lx)\n", rev);
+               ret = -ENODEV;
+               goto out_unmap;
+       }
+       printk(KERN_INFO "VINO Rev: 0x%02lx\n", VINO_REV_NUM(rev));
 
-       err = vino_init();
-       if (err)
-               return err;
+       Vino = (struct vino_video *)
+               kmalloc(sizeof(struct vino_video), GFP_KERNEL);
+       if (!Vino) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       Vino->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!Vino->dummy_page) {
+               ret = -ENOMEM;
+               goto out_free_vino;
+       }
+       for (i = 0; i < 4; i++)
+               Vino->dummy_buf[i] = PHYSADDR(Vino->dummy_page);
+
+       vino->control = 0;
+       /* prevent VINO from throwing spurious interrupts */
+       vino->a.next_4_desc = PHYSADDR(Vino->dummy_buf);
+       vino->b.next_4_desc = PHYSADDR(Vino->dummy_buf);
+       udelay(5);
+       vino->intr_status = 0;
+        /* set threshold level */
+        vino->a.fifo_thres = threshold_a;
+       vino->b.fifo_thres = threshold_b;
+
+       init_MUTEX(&Vino->input_lock);
+
+       if (request_irq(SGI_VINO_IRQ, vino_interrupt, 0, vinostr, NULL)) {
+               printk(KERN_ERR "VINO: irq%02d registration failed\n",
+                      SGI_VINO_IRQ);
+               ret = -EAGAIN;
+               goto out_free_page;
+       }
+
+       ret = vino_i2c_add_bus();
+       if (ret) {
+               printk(KERN_ERR "VINO: I2C bus registration failed\n");
+               goto out_free_irq;
+       }
+
+       if (video_register_device(&Vino->chA.vdev, VFL_TYPE_GRABBER, -1) < 0) {
+               printk("%s, chnl %d: device registration failed.\n",
+                       Vino->chA.vdev.name, Vino->chA.chan);
+               ret = -EINVAL;
+               goto out_i2c_del_bus;
+       }
+       if (video_register_device(&Vino->chB.vdev, VFL_TYPE_GRABBER, -1) < 0) {
+               printk("%s, chnl %d: device registration failed.\n",
+                       Vino->chB.vdev.name, Vino->chB.chan);
+               ret = -EINVAL;
+               goto out_unregister_vdev;
+       }
 
        return 0;
+
+out_unregister_vdev:
+       video_unregister_device(&Vino->chA.vdev);
+out_i2c_del_bus:
+       vino_i2c_del_bus();
+out_free_irq:
+       free_irq(SGI_VINO_IRQ, NULL);
+out_free_page:
+       free_page(Vino->dummy_page);
+out_free_vino:
+       kfree(Vino);
+out_unmap:
+       iounmap(vino);
+
+       return ret;
 }
 
-void cleanup_module(void)
+static void __exit vino_exit(void)
 {
+       video_unregister_device(&Vino->chA.vdev);
+       video_unregister_device(&Vino->chB.vdev);
+       vino_i2c_del_bus();
+       free_irq(SGI_VINO_IRQ, NULL);
+       free_page(Vino->dummy_page);
+       kfree(Vino);
+       iounmap(vino);
 }
-#endif
+
+module_init(vino_init);
+module_exit(vino_exit);
+
+MODULE_DESCRIPTION("Video4Linux driver for SGI Indy VINO (IndyCam)");
+MODULE_LICENSE("GPL");