fuse: lockd support
[safe/jmp/linux-2.6] / drivers / media / video / cafe_ccic.c
index 4aa360b..d99453f 100644 (file)
@@ -3,7 +3,11 @@
  * multifunction chip.  Currently works with the Omnivision OV7670
  * sensor.
  *
+ * The data sheet for this device can be found at:
+ *    http://www.marvell.com/products/pcconn/88ALP01.jsp
+ *
  * Copyright 2006 One Laptop Per Child Association, Inc.
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
  *
  * Written by Jonathan Corbet, corbet@lwn.net.
  *
@@ -13,7 +17,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/pci.h>
@@ -62,13 +65,13 @@ MODULE_SUPPORTED_DEVICE("Video");
  */
 
 #define MAX_DMA_BUFS 3
-static int alloc_bufs_at_load = 0;
-module_param(alloc_bufs_at_load, bool, 0444);
-MODULE_PARM_DESC(alloc_bufs_at_load,
-               "Non-zero value causes DMA buffers to be allocated at module "
-               "load time.  This increases the chances of successfully getting "
-               "those buffers, but at the cost of nailing down the memory from "
-               "the outset.");
+static int alloc_bufs_at_read;
+module_param(alloc_bufs_at_read, bool, 0444);
+MODULE_PARM_DESC(alloc_bufs_at_read,
+               "Non-zero value causes DMA buffers to be allocated when the "
+               "video capture device is read, rather than at module load "
+               "time.  This saves memory, but decreases the chances of "
+               "successfully getting those buffers.");
 
 static int n_dma_bufs = 3;
 module_param(n_dma_bufs, uint, 0644);
@@ -96,7 +99,7 @@ MODULE_PARM_DESC(max_buffers,
                "will be allowed to allocate.  These buffers are big and live "
                "in vmalloc space.");
 
-static int flip = 0;
+static int flip;
 module_param(flip, bool, 0444);
 MODULE_PARM_DESC(flip,
                "If set, the sensor will be instructed to flip the image "
@@ -355,6 +358,7 @@ static int cafe_smbus_write_data(struct cafe_camera *cam,
 {
        unsigned int rval;
        unsigned long flags;
+       DEFINE_WAIT(the_wait);
 
        spin_lock_irqsave(&cam->dev_lock, flags);
        rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
@@ -368,10 +372,29 @@ static int cafe_smbus_write_data(struct cafe_camera *cam,
        rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
        cafe_reg_write(cam, REG_TWSIC1, rval);
        spin_unlock_irqrestore(&cam->dev_lock, flags);
-       msleep(2); /* Required or things flake */
 
+       /*
+        * Time to wait for the write to complete.  THIS IS A RACY
+        * WAY TO DO IT, but the sad fact is that reading the TWSIC1
+        * register too quickly after starting the operation sends
+        * the device into a place that may be kinder and better, but
+        * which is absolutely useless for controlling the sensor.  In
+        * practice we have plenty of time to get into our sleep state
+        * before the interrupt hits, and the worst case is that we
+        * time out and then see that things completed, so this seems
+        * the best way for now.
+        */
+       do {
+               prepare_to_wait(&cam->smbus_wait, &the_wait,
+                               TASK_UNINTERRUPTIBLE);
+               schedule_timeout(1); /* even 1 jiffy is too long */
+               finish_wait(&cam->smbus_wait, &the_wait);
+       } while (!cafe_smbus_write_done(cam));
+
+#ifdef IF_THE_CAFE_HARDWARE_WORKED_RIGHT
        wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
                        CAFE_SMBUS_TIMEOUT);
+#endif
        spin_lock_irqsave(&cam->dev_lock, flags);
        rval = cafe_reg_read(cam, REG_TWSIC1);
        spin_unlock_irqrestore(&cam->dev_lock, flags);
@@ -709,7 +732,7 @@ static void cafe_ctlr_init(struct cafe_camera *cam)
         * Here we must wait a bit for the controller to come around.
         */
        spin_unlock_irqrestore(&cam->dev_lock, flags);
-       mdelay(5);      /* FIXME revisit this */
+       msleep(5);
        spin_lock_irqsave(&cam->dev_lock, flags);
 
        cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
@@ -774,6 +797,12 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam)
        spin_lock_irqsave(&cam->dev_lock, flags);
        cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
        /*
+        * Part one of the sensor dance: turn the global
+        * GPIO signal on.
+        */
+       cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
+       cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL);
+       /*
         * Put the sensor into operational mode (assumes OLPC-style
         * wiring).  Control 0 is reset - set to 1 to operate.
         * Control 1 is power down, set to 0 to operate.
@@ -783,6 +812,7 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam)
        cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
 //     mdelay(1); /* Enough? */
        spin_unlock_irqrestore(&cam->dev_lock, flags);
+       msleep(5); /* Just to be sure */
 }
 
 static void cafe_ctlr_power_down(struct cafe_camera *cam)
@@ -791,6 +821,8 @@ static void cafe_ctlr_power_down(struct cafe_camera *cam)
 
        spin_lock_irqsave(&cam->dev_lock, flags);
        cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
+       cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
+       cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT);
        cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
        spin_unlock_irqrestore(&cam->dev_lock, flags);
 }
@@ -850,6 +882,7 @@ static int cafe_cam_init(struct cafe_camera *cam)
        ret = 0;
        cam->state = S_IDLE;
   out:
+       cafe_ctlr_power_down(cam);
        mutex_unlock(&cam->s_mutex);
        return ret;
 }
@@ -1167,7 +1200,7 @@ static int cafe_setup_siobuf(struct cafe_camera *cam, int index)
        buf->v4lbuf.field = V4L2_FIELD_NONE;
        buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
        /*
-        * Offset: must be 32-bit even on a 64-bit system.  video-buf
+        * Offset: must be 32-bit even on a 64-bit system.  videobuf-dma-sg
         * just uses the length times the index, but the spec warns
         * against doing just that - vma merging problems.  So we
         * leave a gap between each pair of buffers.
@@ -1472,7 +1505,7 @@ static int cafe_v4l_release(struct inode *inode, struct file *filp)
        }
        if (cam->users == 0) {
                cafe_ctlr_power_down(cam);
-               if (! alloc_bufs_at_load)
+               if (alloc_bufs_at_read)
                        cafe_free_dma_bufs(cam);
        }
        mutex_unlock(&cam->s_mutex);
@@ -1560,7 +1593,7 @@ static struct v4l2_pix_format cafe_def_pix_format = {
        .sizeimage      = VGA_WIDTH*VGA_HEIGHT*2,
 };
 
-static int cafe_vidioc_enum_fmt_cap(struct file *filp,
+static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_fmtdesc *fmt)
 {
        struct cafe_camera *cam = priv;
@@ -1575,7 +1608,7 @@ static int cafe_vidioc_enum_fmt_cap(struct file *filp,
 }
 
 
-static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
+static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
                struct v4l2_format *fmt)
 {
        struct cafe_camera *cam = priv;
@@ -1587,7 +1620,7 @@ static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
        return ret;
 }
 
-static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
+static int cafe_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
                struct v4l2_format *fmt)
 {
        struct cafe_camera *cam = priv;
@@ -1602,7 +1635,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
        /*
         * See if the formatting works in principle.
         */
-       ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt);
+       ret = cafe_vidioc_try_fmt_vid_cap(filp, priv, fmt);
        if (ret)
                return ret;
        /*
@@ -1637,7 +1670,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
  * The V4l2 spec wants us to be smarter, and actually get this from
  * the camera (and not mess with it at open time).  Someday.
  */
-static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv,
+static int cafe_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
                struct v4l2_format *f)
 {
        struct cafe_camera *cam = priv;
@@ -1747,10 +1780,10 @@ static struct video_device cafe_v4l_template = {
        .release = cafe_v4l_dev_release,
 
        .vidioc_querycap        = cafe_vidioc_querycap,
-       .vidioc_enum_fmt_cap    = cafe_vidioc_enum_fmt_cap,
-       .vidioc_try_fmt_cap     = cafe_vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap       = cafe_vidioc_s_fmt_cap,
-       .vidioc_g_fmt_cap       = cafe_vidioc_g_fmt_cap,
+       .vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = cafe_vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = cafe_vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = cafe_vidioc_g_fmt_vid_cap,
        .vidioc_enum_input      = cafe_vidioc_enum_input,
        .vidioc_g_input         = cafe_vidioc_g_input,
        .vidioc_s_input         = cafe_vidioc_s_input,
@@ -2102,10 +2135,16 @@ static int cafe_pci_probe(struct pci_dev *pdev,
        ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
        if (ret)
                goto out_iounmap;
+       /*
+        * Initialize the controller and leave it powered up.  It will
+        * stay that way until the sensor driver shows up.
+        */
        cafe_ctlr_init(cam);
        cafe_ctlr_power_up(cam);
        /*
-        * Set up I2C/SMBUS communications
+        * Set up I2C/SMBUS communications.  We have to drop the mutex here
+        * because the sensor could attach in this call chain, leading to
+        * unsightly deadlocks.
         */
        mutex_unlock(&cam->s_mutex);  /* attach can deadlock */
        ret = cafe_smbus_setup(cam);
@@ -2125,7 +2164,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
        /*
         * If so requested, try to get our DMA buffers now.
         */
-       if (alloc_bufs_at_load) {
+       if (!alloc_bufs_at_read) {
                if (cafe_alloc_dma_bufs(cam, 1))
                        cam_warn(cam, "Unable to alloc DMA buffers at load"
                                        " will try again later.");
@@ -2196,13 +2235,16 @@ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct cafe_camera *cam = cafe_find_by_pdev(pdev);
        int ret;
+       enum cafe_state cstate;
 
        ret = pci_save_state(pdev);
        if (ret)
                return ret;
+       cstate = cam->state; /* HACK - stop_dma sets to idle */
        cafe_ctlr_stop_dma(cam);
        cafe_ctlr_power_down(cam);
        pci_disable_device(pdev);
+       cam->state = cstate;
        return 0;
 }
 
@@ -2215,9 +2257,22 @@ static int cafe_pci_resume(struct pci_dev *pdev)
        ret = pci_restore_state(pdev);
        if (ret)
                return ret;
-       pci_enable_device(pdev);
+       ret = pci_enable_device(pdev);
+
+       if (ret) {
+               cam_warn(cam, "Unable to re-enable device on resume!\n");
+               return ret;
+       }
        cafe_ctlr_init(cam);
-       cafe_ctlr_power_up(cam);
+       cafe_ctlr_power_down(cam);
+
+       mutex_lock(&cam->s_mutex);
+       if (cam->users > 0) {
+               cafe_ctlr_power_up(cam);
+               __cafe_cam_reset(cam);
+       }
+       mutex_unlock(&cam->s_mutex);
+
        set_bit(CF_CONFIG_NEEDED, &cam->flags);
        if (cam->state == S_SPECREAD)
                cam->state = S_IDLE;  /* Don't bother restarting */