V4L/DVB (6159): General code cleanup
[safe/jmp/linux-2.6] / drivers / media / video / cafe_ccic.c
index dc6e16c..0fae0e0 100644 (file)
@@ -4,6 +4,7 @@
  * sensor.
  *
  * 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 +14,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>
@@ -22,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
 #include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/list.h>
@@ -36,7 +37,7 @@
 
 #include "cafe_ccic-regs.h"
 
-#define CAFE_VERSION 0x000001
+#define CAFE_VERSION 0x000002
 
 
 /*
@@ -164,7 +165,7 @@ struct cafe_camera
        struct tasklet_struct s_tasklet;
 
        /* Current operating parameters */
-       enum v4l2_chip_ident sensor_type;               /* Currently ov7670 only */
+       u32 sensor_type;                /* Currently ov7670 only */
        struct v4l2_pix_format pix_format;
 
        /* Locks */
@@ -354,6 +355,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);
@@ -367,10 +369,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);
@@ -549,6 +570,7 @@ static int cafe_smbus_setup(struct cafe_camera *cam)
        adap->client_unregister = cafe_smbus_detach;
        adap->algo = &cafe_smbus_algo;
        strcpy(adap->name, "cafe_ccic");
+       adap->dev.parent = &cam->pdev->dev;
        i2c_set_adapdata(adap, cam);
        ret = i2c_add_adapter(adap);
        if (ret)
@@ -612,16 +634,6 @@ static void cafe_ctlr_image(struct cafe_camera *cam)
                            C0_DF_MASK);
            break;
 
-       /*
-        * For "fake rgb32" get the image pitch right.
-        */
-       case V4L2_PIX_FMT_RGB32:
-           cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline/2,
-                           IMGP_YP_MASK);
-           imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
-                   ((fmt->bytesperline/2) & IMGSZ_H_MASK);
-           cafe_reg_write(cam, REG_IMGSIZE, imgsz);
-           /* fall into ... */
        case V4L2_PIX_FMT_RGB444:
            cafe_reg_write_mask(cam, REG_CTRL0,
                            C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
@@ -713,7 +725,13 @@ static void cafe_ctlr_init(struct cafe_camera *cam)
        cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
        cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
        cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
-       mdelay(5);      /* FIXME revisit this */
+       /*
+        * Here we must wait a bit for the controller to come around.
+        */
+       spin_unlock_irqrestore(&cam->dev_lock, flags);
+       msleep(5);
+       spin_lock_irqsave(&cam->dev_lock, flags);
+
        cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
        cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
        /*
@@ -776,15 +794,22 @@ 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.
         */
        cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
-       mdelay(1); /* Marvell says 1ms will do it */
+//     mdelay(1); /* Marvell says 1ms will do it */
        cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
-       mdelay(1); /* Enough? */
+//     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)
@@ -793,6 +818,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);
 }
@@ -827,6 +854,7 @@ static int __cafe_cam_reset(struct cafe_camera *cam)
  */
 static int cafe_cam_init(struct cafe_camera *cam)
 {
+       struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 };
        int ret;
 
        mutex_lock(&cam->s_mutex);
@@ -836,9 +864,11 @@ static int cafe_cam_init(struct cafe_camera *cam)
        ret = __cafe_cam_reset(cam);
        if (ret)
                goto out;
-       ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type);
+       chip.match_chip = cam->sensor->addr;
+       ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip);
        if (ret)
                goto out;
+       cam->sensor_type = chip.ident;
 //     if (cam->sensor->addr != OV7xx0_SID) {
        if (cam->sensor_type != V4L2_IDENT_OV7670) {
                cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
@@ -849,6 +879,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;
 }
@@ -902,11 +933,8 @@ static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime)
        cafe_set_config_needed(cam, 1);
        if (loadtime)
                cam->dma_buf_size = dma_buf_size;
-       else {
+       else
                cam->dma_buf_size = cam->pix_format.sizeimage;
-               if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32)
-                       cam->dma_buf_size /= 2;
-       }
        if (n_dma_bufs > 3)
                n_dma_bufs = 3;
 
@@ -1034,7 +1062,7 @@ static ssize_t cafe_v4l_read(struct file *filp,
                char __user *buffer, size_t len, loff_t *pos)
 {
        struct cafe_camera *cam = filp->private_data;
-       int ret;
+       int ret = 0;
 
        /*
         * Perhaps we're in speculative read mode and already
@@ -1207,7 +1235,7 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
                struct v4l2_requestbuffers *req)
 {
        struct cafe_camera *cam = filp->private_data;
-       int ret;
+       int ret = 0;  /* Silence warning */
 
        /*
         * Make sure it's something we can do.  User pointers could be
@@ -1263,8 +1291,6 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
 
        if (cam->n_sbufs == 0)  /* no luck at all - ret already set */
                kfree(cam->sb_bufs);
-       else
-               ret = 0;
        req->count = cam->n_sbufs;  /* In case of partial success */
 
   out:
@@ -1679,22 +1705,40 @@ static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 }
 
 /* from vivi.c */
-static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a)
+static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
 {
        return 0;
 }
 
-
 /*
- * The TV Norm stuff is weird - we're a camera with little to do with TV,
- * really.  The following is what vivi does.
+ * G/S_PARM.  Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
  */
-static struct v4l2_tvnorm cafe_tvnorm[] = {
-       {
-               .name      = "NTSC-M",
-               .id        = V4L2_STD_NTSC_M,
-       }
-};
+static int cafe_vidioc_g_parm(struct file *filp, void *priv,
+               struct v4l2_streamparm *parms)
+{
+       struct cafe_camera *cam = priv;
+       int ret;
+
+       mutex_lock(&cam->s_mutex);
+       ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms);
+       mutex_unlock(&cam->s_mutex);
+       parms->parm.capture.readbuffers = n_dma_bufs;
+       return ret;
+}
+
+static int cafe_vidioc_s_parm(struct file *filp, void *priv,
+               struct v4l2_streamparm *parms)
+{
+       struct cafe_camera *cam = priv;
+       int ret;
+
+       mutex_lock(&cam->s_mutex);
+       ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms);
+       mutex_unlock(&cam->s_mutex);
+       parms->parm.capture.readbuffers = n_dma_bufs;
+       return ret;
+}
 
 
 static void cafe_v4l_dev_release(struct video_device *vd)
@@ -1710,7 +1754,7 @@ static void cafe_v4l_dev_release(struct video_device *vd)
  * clone it for specific real devices.
  */
 
-static struct file_operations cafe_v4l_fops = {
+static const struct file_operations cafe_v4l_fops = {
        .owner = THIS_MODULE,
        .open = cafe_v4l_open,
        .release = cafe_v4l_release,
@@ -1726,8 +1770,7 @@ static struct video_device cafe_v4l_template = {
        .type = VFL_TYPE_GRABBER,
        .type2 = VID_TYPE_CAPTURE,
        .minor = -1, /* Get one dynamically */
-       .tvnorms = cafe_tvnorm,
-       .tvnormsize = 1,
+       .tvnorms = V4L2_STD_NTSC_M,
        .current_norm = V4L2_STD_NTSC_M,  /* make mplayer happy */
 
        .fops = &cafe_v4l_fops,
@@ -1751,7 +1794,8 @@ static struct video_device cafe_v4l_template = {
        .vidioc_queryctrl       = cafe_vidioc_queryctrl,
        .vidioc_g_ctrl          = cafe_vidioc_g_ctrl,
        .vidioc_s_ctrl          = cafe_vidioc_s_ctrl,
-       /* Do cropping someday */
+       .vidioc_g_parm          = cafe_vidioc_g_parm,
+       .vidioc_s_parm          = cafe_vidioc_s_parm,
 };
 
 
@@ -1765,26 +1809,6 @@ static struct video_device cafe_v4l_template = {
  * Interrupt handler stuff
  */
 
-/*
- * Create RGB32 from RGB444 so it can be displayed before the applications
- * know about the latter format.
- */
-static void cafe_fake_rgb32(struct cafe_camera *cam, char *dest, char *src)
-{
-       int i;
-       u16 *ssrc = (u16 *) src;
-
-       /* RGB444 version */
-       for (i = 0; i < cam->pix_format.sizeimage; i += 4) {
-       //              dest[0] = (*ssrc & 0xf000) >> 8;
-               dest[0] = (*ssrc & 0x000f) << 4;
-               dest[1] = (*ssrc & 0x00f0);
-               dest[2] = (*ssrc & 0x0f00) >> 4;
-               dest[3] = (*ssrc & 0xf000);   /* Alpha */
-               dest += 4;
-               ssrc++;
-       }
-}
 
 
 static void cafe_frame_tasklet(unsigned long data)
@@ -1808,21 +1832,19 @@ static void cafe_frame_tasklet(unsigned long data)
                if (list_empty(&cam->sb_avail))
                        break;  /* Leave it valid, hope for better later */
                clear_bit(bufno, &cam->flags);
-               /*
-                * We could perhaps drop the spinlock during this
-                * big copy.  Something to consider.
-                */
                sbuf = list_entry(cam->sb_avail.next,
                                struct cafe_sio_buffer, list);
-               if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32)
-                       cafe_fake_rgb32(cam, sbuf->buffer, cam->dma_bufs[bufno]);
-               else
-                       memcpy(sbuf->buffer, cam->dma_bufs[bufno],
-                                       cam->pix_format.sizeimage);
+               /*
+                * Drop the lock during the big copy.  This *should* be safe...
+                */
+               spin_unlock_irqrestore(&cam->dev_lock, flags);
+               memcpy(sbuf->buffer, cam->dma_bufs[bufno],
+                               cam->pix_format.sizeimage);
                sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
                sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
                sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
                sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+               spin_lock_irqsave(&cam->dev_lock, flags);
                list_move_tail(&sbuf->list, &cam->sb_full);
        }
        if (! list_empty(&cam->sb_full))
@@ -1987,7 +2009,7 @@ static ssize_t cafe_dfs_read_regs(struct file *file,
                        s - cafe_debug_buf);
 }
 
-static struct file_operations cafe_dfs_reg_ops = {
+static const struct file_operations cafe_dfs_reg_ops = {
        .owner = THIS_MODULE,
        .read = cafe_dfs_read_regs,
        .open = cafe_dfs_open
@@ -2013,7 +2035,7 @@ static ssize_t cafe_dfs_read_cam(struct file *file,
                        s - cafe_debug_buf);
 }
 
-static struct file_operations cafe_dfs_cam_ops = {
+static const struct file_operations cafe_dfs_cam_ops = {
        .owner = THIS_MODULE,
        .read = cafe_dfs_read_cam,
        .open = cafe_dfs_open
@@ -2110,10 +2132,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);
@@ -2126,6 +2154,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
        cam->v4ldev = cafe_v4l_template;
        cam->v4ldev.debug = 0;
 //     cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
+       cam->v4ldev.dev = &pdev->dev;
        ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
        if (ret)
                goto out_smbus;
@@ -2184,7 +2213,7 @@ static void cafe_pci_remove(struct pci_dev *pdev)
        struct cafe_camera *cam = cafe_find_by_pdev(pdev);
 
        if (cam == NULL) {
-               cam_warn(cam, "pci_remove on unknown pdev %p\n", pdev);
+               printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
                return;
        }
        mutex_lock(&cam->s_mutex);
@@ -2195,10 +2224,61 @@ static void cafe_pci_remove(struct pci_dev *pdev)
 }
 
 
+#ifdef CONFIG_PM
+/*
+ * Basic power management.
+ */
+static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+       int ret;
+
+       ret = pci_save_state(pdev);
+       if (ret)
+               return ret;
+       cafe_ctlr_stop_dma(cam);
+       cafe_ctlr_power_down(cam);
+       pci_disable_device(pdev);
+       return 0;
+}
+
+
+static int cafe_pci_resume(struct pci_dev *pdev)
+{
+       struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+       int ret = 0;
+
+       ret = pci_restore_state(pdev);
+       if (ret)
+               return ret;
+       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_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 */
+       else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING)
+               ret = cafe_read_setup(cam, cam->state);
+       return ret;
+}
+
+#endif  /* CONFIG_PM */
 
 
 static struct pci_device_id cafe_ids[] = {
-       { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */
        { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
        { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
        { 0, }
@@ -2211,6 +2291,10 @@ static struct pci_driver cafe_pci_driver = {
        .id_table = cafe_ids,
        .probe = cafe_pci_probe,
        .remove = cafe_pci_remove,
+#ifdef CONFIG_PM
+       .suspend = cafe_pci_suspend,
+       .resume = cafe_pci_resume,
+#endif
 };