]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
Add OMAP camera driver
authorTony Lindgren <tony@atomide.com>
Mon, 9 May 2005 21:38:42 +0000 (14:38 -0700)
committerTony Lindgren <tony@atomide.com>
Mon, 9 May 2005 21:38:42 +0000 (14:38 -0700)
Adds OMAP camera driver.

Signed-off-by: Tony Lindgren <tony@atomide.com>
12 files changed:
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/omap/Makefile [new file with mode: 0644]
drivers/media/video/omap/camera_core.c [new file with mode: 0644]
drivers/media/video/omap/camera_core.h [new file with mode: 0644]
drivers/media/video/omap/camera_hw_if.h [new file with mode: 0644]
drivers/media/video/omap/omap16xxcam.c [new file with mode: 0644]
drivers/media/video/omap/omap16xxcam.h [new file with mode: 0644]
drivers/media/video/omap/ov9640.h [new file with mode: 0644]
drivers/media/video/omap/sensor_if.h [new file with mode: 0644]
drivers/media/video/omap/sensor_ov9640.c [new file with mode: 0644]
include/asm-arm/arch-omap/camera.h [new file with mode: 0644]

index d3dd4228b72d46c72a2ee7bbe7c4e6a2dfd03ba0..2bf585dbb236dcc158a38dd56435bdd3037b8301 100644 (file)
@@ -358,4 +358,10 @@ config VIDEO_M32R_AR_M64278
          Say Y here to use the Renesas M64278E-800 camera module,
          which supports VGA(640x480 pixcels) size of images.
 
+config VIDEO_OMAP_CAMERA
+       tristate "OMAP Video for Linux camera driver"
+       depends on VIDEO_DEV && ARCH_OMAP16XX
+       select VIDEO_BUF
+
+
 endmenu
index 2dc906fdfa550948caf23f528119401b7de22f9f..9c5e92c002cef2e9e1b03f999f662bfe96a30e27 100644 (file)
@@ -52,5 +52,6 @@ obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 
 obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
+obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omap/
 
 EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core
diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile
new file mode 100644 (file)
index 0000000..1f6476a
--- /dev/null
@@ -0,0 +1,7 @@
+# Makefile for camera driver for H2/H3
+
+omapCamera-objs :=  camera_core.o omap16xxcam.o sensor_ov9640.o  
+
+obj-y += omapCamera.o
+
+EXTRA_CFLAGS = -I$(src)/..
diff --git a/drivers/media/video/omap/camera_core.c b/drivers/media/video/omap/camera_core.c
new file mode 100644 (file)
index 0000000..66ccd80
--- /dev/null
@@ -0,0 +1,1197 @@
+/*
+ * drivers/media/video/omap/camera_core.c
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ *
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP H2 and H3 camera controller.
+ *
+ * Adapted from omap24xx driver written by Andy Lowe (source@mvista.com)
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ * 
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ *
+ * History:
+ *   27/03/05   Vladimir Barinov - Added support for power management
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/videodev.h>
+#include <linux/pci.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+
+#include "sensor_if.h"
+#include "camera_hw_if.h"
+#include "camera_core.h"
+struct camera_device *camera_dev;
+extern struct camera_sensor camera_sensor_if;
+extern struct camera_hardware camera_hardware_if;
+static void camera_core_sgdma_process(struct camera_device *cam);
+
+/* module parameters */
+static int video_nr = -1;      /* video device minor (-1 ==> auto assign) */
+
+/* Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA.
+ */
+static int capture_mem = 1280*960*2*2;
+
+/*Size of video overlay framebuffer. This determines the maximum image size
+ *that can be previewed. Default is 600KB, enough for sxga.
+ */
+static int overlay_mem = 640*480*2;
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+/* This function is called when a scatter DMA fragment is completed */
+static void
+camera_core_callback_sgdma(void *arg1, void *arg2)
+{
+       struct camera_device *cam = (struct camera_device *)arg1;
+       int sgslot = (int)arg2;
+
+       struct sgdma_state *sgdma;
+
+       spin_lock(&cam->sg_lock);
+       sgdma = cam->sgdma + sgslot;
+       if (!sgdma->queued_sglist)
+       {
+               spin_unlock(&cam->sg_lock);
+               printk(KERN_ERR CAM_NAME ": SGDMA completed when none queued\n");
+               return;
+       }
+       if (!--sgdma->queued_sglist) {
+               /* queue for this sglist is empty so check whether transfer
+               ** of the frame has been completed */
+               if (sgdma->next_sglist == sgdma->sglen) {
+                       dma_callback_t callback = sgdma->callback;
+                       void *arg = sgdma->arg;
+                       /* all done with this sglist */
+                       cam->free_sgdma++;
+                       if (callback) {
+                               spin_unlock(&cam->sg_lock);
+                               (*callback)(cam, arg);
+                               camera_core_sgdma_process(cam);
+                               return;
+                       }
+               }
+       }
+       spin_unlock(&cam->sg_lock);
+       camera_core_sgdma_process(cam);
+
+       return;
+}
+
+static void
+camera_core_sgdma_init(struct camera_device *cam)
+{
+       int sg;
+
+       /* Initialize the underlying camera DMA */
+       cam->cam_hardware->init_dma(cam->hardware_data);
+       spin_lock_init(&cam->sg_lock);
+       
+       cam->free_sgdma = NUM_SG_DMA;
+       cam->next_sgdma = 0;
+       for (sg = 0; sg < NUM_SG_DMA; sg++) {
+               cam->sgdma[sg].sglen = 0;
+               cam->sgdma[sg].next_sglist = 0;
+               cam->sgdma[sg].queued_sglist = 0;
+               cam->sgdma[sg].csr = 0;
+               cam->sgdma[sg].callback = NULL;
+               cam->sgdma[sg].arg = NULL;
+       }
+}
+
+/*
+ * Process the scatter-gather DMA queue by starting queued transfers
+ * This function is called to program the dma to start the transfer of an image.
+ */
+static void
+camera_core_sgdma_process(struct camera_device *cam)
+{
+       unsigned long irqflags;
+       int queued_sgdma, sgslot;
+       struct sgdma_state *sgdma;
+       const struct scatterlist *sglist;
+       
+       spin_lock_irqsave(&cam->sg_lock, irqflags);
+       if (1 == cam->in_use) {
+               spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+               return;
+       }
+       cam->in_use = 1;
+       spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+
+       queued_sgdma = NUM_SG_DMA - cam->free_sgdma;
+       sgslot = (cam->next_sgdma + cam->free_sgdma) % (NUM_SG_DMA);
+       while (queued_sgdma > 0) {
+               sgdma = cam->sgdma + sgslot;
+               while (sgdma->next_sglist < sgdma->sglen) {
+                       sglist = sgdma->sglist + sgdma->next_sglist;
+                       if (cam->cam_hardware->start_dma(sgdma, camera_core_callback_sgdma,
+                               (void *)cam, (void *)sgslot, cam->hardware_data)) {
+                                       /* dma start failed */
+                                       cam->in_use = 0;
+                                       return;
+                       }
+                       else {
+                               /* dma start successful */
+                               sgdma->next_sglist ++;
+                               sgdma->queued_sglist ++;
+                       }
+               }
+               queued_sgdma-- ;
+               sgslot = (sgslot + 1) % (NUM_SG_DMA);
+       }
+
+       cam->in_use = 0;
+}
+
+/* Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or
+ * non-zero if all of the scatter-gather slots are already in use.
+ */
+static int
+camera_core_sgdma_queue(struct camera_device *cam,
+        const struct scatterlist *sglist, int sglen, dma_callback_t callback,
+        void *arg)
+{
+       unsigned long irqflags;
+       struct sgdma_state *sgdma;
+
+       if ((sglen < 0) || ((sglen > 0) & !sglist))
+               return -EINVAL;
+
+       spin_lock_irqsave(&cam->sg_lock, irqflags);
+
+       if (!cam->free_sgdma) {
+               spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+               return -EBUSY;
+       }
+
+       sgdma = cam->sgdma + cam->next_sgdma;
+
+       sgdma->sglist = sglist;
+       sgdma->sglen = sglen;
+       sgdma->next_sglist = 0;
+       sgdma->queued_sglist = 0;
+       sgdma->csr = 0;
+       sgdma->callback = callback;
+       sgdma->arg = arg;
+
+       cam->next_sgdma = (cam->next_sgdma + 1) % (NUM_SG_DMA); 
+       cam->free_sgdma--;
+
+       spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+
+       camera_core_sgdma_process(cam);
+
+       return 0;
+}
+
+
+/* -------------------overlay routines ------------------------------*/
+/* callback routine for overlay DMA completion. We just start another DMA
+ * transfer unless overlay has been turned off
+ */
+
+static void
+camera_core_overlay_callback(void *arg1, void *arg)
+{
+       struct camera_device *cam = (struct camera_device *)arg1;
+       int err;
+       unsigned long irqflags;
+       int i, j;
+       int count, index;
+       unsigned char *fb_buf = phys_to_virt((unsigned long)camera_dev->fbuf.base);
+
+       spin_lock_irqsave(&cam->overlay_lock, irqflags);
+
+       if (!cam->previewing || cam->overlay_cnt == 0) {
+               spin_unlock_irqrestore(&cam->overlay_lock, irqflags);
+               return;
+       }
+
+       --cam->overlay_cnt;
+       sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys;
+       sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage;
+
+       count = 0;
+       j = ((cam->pix.width - 1) * cam->fbuf.fmt.bytesperline);
+       for (i = 0 ; i < cam->pix.sizeimage; i += cam->pix.bytesperline) {
+               for (index = 0; index < cam->pix.bytesperline; index++) {
+                       fb_buf[j] = *(((unsigned char *) cam->overlay_base) +
+                                                                i + index);
+                       index++;
+                       fb_buf[j + 1] = *(((unsigned char *) cam->overlay_base) + i + index);
+                       j = j - cam->fbuf.fmt.bytesperline;
+               }
+               count += 2;
+               j = ((cam->pix.width - 1) * cam->fbuf.fmt.bytesperline) + count;
+       }
+
+       while (cam->overlay_cnt < 2) {
+               err = camera_core_sgdma_queue(cam, &cam->overlay_sglist, 1,
+                       camera_core_overlay_callback, NULL);
+               if (err)
+                       break;
+               ++cam->overlay_cnt;
+       }
+
+       spin_unlock_irqrestore(&cam->overlay_lock, irqflags);
+
+}
+
+static void
+camera_core_start_overlay(struct camera_device *cam)
+{
+       int err;
+       unsigned long irqflags;
+
+       if (!cam->previewing) 
+               return;
+
+       spin_lock_irqsave(&cam->overlay_lock, irqflags);
+
+       sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys;
+       sg_dma_len(&cam->overlay_sglist)= cam->pix.sizeimage;
+       while (cam->overlay_cnt < 2) {
+               err = camera_core_sgdma_queue(cam, &cam->overlay_sglist, 1,
+                               camera_core_overlay_callback, NULL);
+               if (err)
+                       break;
+               ++cam->overlay_cnt;
+       }
+
+       spin_unlock_irqrestore(&cam->overlay_lock, irqflags);
+}
+
+/* ------------------ videobuf_queue_ops ---------------------------------------- */
+
+/* This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void
+camera_core_vbq_complete(void *arg1, void *arg)
+{
+       struct camera_device *cam = (struct camera_device *)arg1;
+       struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+
+       spin_lock(&cam->vbq_lock);
+
+       do_gettimeofday(&vb->ts);
+       vb->field_count = cam->field_count;
+       cam->field_count += 2;
+       vb->state = STATE_DONE;
+
+       wake_up(&vb->done);
+       
+       spin_unlock(&cam->vbq_lock);
+}
+
+static void
+camera_core_vbq_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       videobuf_waiton(vb, 0, 0);
+       videobuf_dma_pci_unmap(NULL, &vb->dma);
+       videobuf_dma_free(&vb->dma);
+
+       vb->state = STATE_NEEDS_INIT;
+}
+
+/* Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int
+camera_core_vbq_setup(struct videobuf_queue *q, unsigned int *cnt, unsigned int *size)
+{
+       struct camera_device *cam = q->priv_data;
+
+       if (*cnt <= 0)
+               *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+       if (*cnt > VIDEO_MAX_FRAME)
+               *cnt = VIDEO_MAX_FRAME;
+
+       spin_lock(&cam->img_lock);
+       *size = cam->pix.sizeimage;
+       spin_unlock(&cam->img_lock);
+
+       while (*size * *cnt > capture_mem)
+               (*cnt)--;
+
+       return 0;
+}
+
+static int
+camera_core_vbq_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+        enum v4l2_field field)
+{
+       struct camera_device *cam = q->priv_data;
+       int err = 0;
+
+       spin_lock(&cam->img_lock);
+       if (cam->pix.sizeimage > vb->bsize) {
+               spin_unlock(&cam->img_lock);
+               return -EINVAL;
+       }
+       vb->size = cam->pix.sizeimage; 
+       vb->width = cam->pix.width;
+       vb->height = cam->pix.height;
+       vb->field = field;
+       spin_unlock(&cam->img_lock);
+
+       if (vb->state == STATE_NEEDS_INIT)
+               err = videobuf_iolock(NULL, vb, NULL);
+
+       if (!err)
+               vb->state = STATE_PREPARED;
+       else
+               camera_core_vbq_release (q, vb);
+
+       return err;
+}
+
+static void
+camera_core_vbq_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct camera_device *cam = q->priv_data;
+       enum videobuf_state state = vb->state;
+       int err;
+
+       vb->state = STATE_QUEUED;
+       err = camera_core_sgdma_queue(cam, vb->dma.sglist, vb->dma.sglen,
+                camera_core_vbq_complete, vb);
+       if (err) {
+               /* Oops.  We're not supposed to get any errors here.  The only
+               * way we could get an error is if we ran out of scatter-gather
+               * DMA slots, but we are supposed to have at least as many
+               * scatter-gather DMA slots as video buffers so that can't
+               * happen.
+               */
+               printk(KERN_DEBUG CAM_NAME
+                       ": Failed to queue a video buffer for SGDMA\n");
+               vb->state = state;
+       }
+}
+
+/* ------------------ videobuf_queue_ops ---------------------------------------- */
+
+static int
+camera_core_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 
+                    void *arg)
+{
+       struct camera_fh *fh  = file->private_data;
+       struct camera_device *cam = fh->cam;
+       int err;
+
+       switch (cmd) {
+               case VIDIOC_ENUMINPUT:
+               {
+                       /* default handler assumes 1 video input (the camera) */
+                       struct v4l2_input *input = (struct v4l2_input *)arg;
+                       int index = input->index;
+
+                       memset(input, 0, sizeof(*input));
+                       input->index = index;
+
+                       if (index > 0)
+                               return -EINVAL;
+
+                       strlcpy(input->name, "camera", sizeof(input->name));
+                       input->type = V4L2_INPUT_TYPE_CAMERA;
+
+                       return 0;
+               }
+
+               case VIDIOC_G_INPUT:
+               {
+                       unsigned int *input = arg;
+                       *input = 0;
+
+                       return 0;
+               }
+
+               case VIDIOC_S_INPUT:
+               {
+                       unsigned int *input = arg;
+
+                       if (*input > 0)
+                               return -EINVAL;
+
+                       return 0;
+               }
+
+               case VIDIOC_ENUM_FMT:
+               {
+                       struct v4l2_fmtdesc *fmt = arg;
+                       return cam->cam_sensor->enum_pixformat(fmt, cam->sensor_data);
+               }       
+
+               case VIDIOC_TRY_FMT:
+               {
+                       struct v4l2_format *fmt = arg;
+                       return cam->cam_sensor->try_format(&fmt->fmt.pix, cam->sensor_data);
+
+               }
+
+               case VIDIOC_G_FMT:
+               {
+                       struct v4l2_format *fmt = arg;
+
+                       /* get the current format */
+                       memset(&fmt->fmt.pix, 0, sizeof (fmt->fmt.pix));
+                       fmt->fmt.pix = cam->pix;
+                       
+                       return 0;
+               }
+
+               case VIDIOC_S_FMT:
+               {
+                       struct v4l2_format *fmt = arg;
+                       unsigned int temp_sizeimage = 0;
+
+                       temp_sizeimage = cam->pix.sizeimage;
+                       cam->cam_sensor->try_format(&fmt->fmt.pix, cam->sensor_data);
+                       cam->pix = fmt->fmt.pix;
+
+                       cam->xclk = cam->cam_sensor->calc_xclk(&cam->pix,
+                               &cam->nominal_timeperframe, cam->sensor_data);
+                       cam->cparm.timeperframe = cam->nominal_timeperframe;
+                       cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data);
+                       return cam->cam_sensor->configure(&cam->pix, cam->xclk, 
+                                               &cam->cparm.timeperframe, cam->sensor_data);
+               }
+
+               case VIDIOC_QUERYCTRL:
+               {
+                       struct v4l2_queryctrl *qc = arg;
+                       return cam->cam_sensor->query_control(qc, cam->sensor_data);
+               }
+
+               case VIDIOC_G_CTRL:
+               {
+                       struct v4l2_control *vc = arg;
+                       return cam->cam_sensor->get_control(vc, cam->sensor_data);
+               }
+
+               case VIDIOC_S_CTRL:
+               {
+                       struct v4l2_control *vc = arg;
+                       return cam->cam_sensor->set_control(vc, cam->sensor_data);
+               }
+               
+               case VIDIOC_QUERYCAP:
+               {
+                       struct v4l2_capability *cap = 
+                               (struct v4l2_capability *) arg;
+
+                       memset(cap, 0, sizeof(*cap));
+                       strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+                       strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+                       cap->bus_info[0] = '\0';
+                       cap->version = KERNEL_VERSION(0, 0, 0);
+                       cap->capabilities =
+                               V4L2_CAP_VIDEO_CAPTURE |
+                               V4L2_CAP_VIDEO_OVERLAY |
+                               V4L2_CAP_READWRITE | 
+                               V4L2_CAP_STREAMING;
+                       return 0;
+               }
+
+               case VIDIOC_G_FBUF: /* Get the frame buffer parameters */
+               {
+                       struct v4l2_framebuffer *fbuf =
+                               (struct v4l2_framebuffer *) arg;
+
+                       spin_lock(&cam->img_lock);
+                       *fbuf = cam->fbuf;
+                       spin_unlock(&cam->img_lock);
+                       return 0;
+               }
+
+               case VIDIOC_S_FBUF: /* set the frame buffer parameters */
+               {
+                       struct v4l2_framebuffer *fbuf =
+                               (struct v4l2_framebuffer *) arg;
+
+                       spin_lock(&cam->img_lock);
+                       if (cam->previewing) {
+                               spin_unlock(&cam->img_lock);
+                               return -EBUSY;
+                       }
+                       cam->fbuf.base = fbuf->base;
+                       cam->fbuf.fmt = fbuf->fmt;      
+                       
+                       spin_unlock(&cam->img_lock);
+                       return 0;
+               }
+
+               case VIDIOC_OVERLAY:
+               {
+                       int enable = *((int *) arg);
+
+                       /* 
+                        * check whether the capture format and 
+                        ** the display format matches 
+                        * return failure if they are different
+                        */
+                       if (cam->pix.pixelformat != cam->fbuf.fmt.pixelformat)
+                       {
+                               return -EINVAL;
+                       }
+
+                       /* If the camera image size is greater 
+                       ** than LCD size return failure */
+                       if ((cam->pix.width > cam->fbuf.fmt.height) || 
+                               (cam->pix.height > cam->fbuf.fmt.width))
+                       {
+                               return -EINVAL;
+                       }
+                       
+                       if (!cam->previewing && enable)
+                       {
+                               cam->previewing = fh;
+                               cam->overlay_cnt = 0;
+                               camera_core_start_overlay(cam);
+                       }
+                       else if (!enable)
+                       {
+                               cam->previewing = NULL;
+                       }
+       
+                       return 0;
+               }
+
+               case VIDIOC_REQBUFS:
+                       return videobuf_reqbufs(&fh->vbq, arg);
+
+               case VIDIOC_QUERYBUF:
+                       return videobuf_querybuf(&fh->vbq, arg);
+
+               case VIDIOC_QBUF:
+                       return videobuf_qbuf(&fh->vbq, arg);
+
+               case VIDIOC_DQBUF:
+                       return videobuf_dqbuf(&fh->vbq, arg,
+          file->f_flags & O_NONBLOCK);
+
+               case VIDIOC_STREAMON:
+               {
+                       spin_lock(&cam->img_lock);
+
+                       if (cam->streaming || cam->reading) {
+                               spin_unlock(&cam->img_lock);
+                               return -EBUSY;
+                       }
+                       else {
+                               cam->streaming = fh;
+                               /* FIXME: start camera interface */
+                       }
+
+                       spin_unlock(&cam->img_lock);
+
+                       return videobuf_streamon(&fh->vbq);
+               }
+               case VIDIOC_STREAMOFF:
+               {
+                       err = videobuf_streamoff(&fh->vbq);
+                       if (err < 0)
+                               return err;
+
+                       spin_lock(&cam->img_lock);
+                       if (cam->streaming == fh) {
+                               cam->streaming = NULL;
+                               /* FIXME: stop camera interface */
+                       }
+                       spin_unlock(&cam->img_lock);
+                       return 0;
+               }
+               case VIDIOC_ENUMSTD:
+               case VIDIOC_G_STD:
+               case VIDIOC_S_STD:
+               case VIDIOC_QUERYSTD:
+               {
+                       /* Digital cameras don't have an analog video standard, 
+                        * so we don't need to implement these ioctls.
+                        */
+                        return -EINVAL;
+               }
+               case VIDIOC_G_AUDIO:
+               case VIDIOC_S_AUDIO:
+               case VIDIOC_G_AUDOUT:
+               case VIDIOC_S_AUDOUT:
+               {
+                       /* we don't have any audio inputs or outputs */
+                       return -EINVAL;
+               }
+
+               case VIDIOC_G_JPEGCOMP:
+               case VIDIOC_S_JPEGCOMP:
+               {
+                       /* JPEG compression is not supported */
+                       return -EINVAL;
+               }
+
+               case VIDIOC_G_TUNER:
+               case VIDIOC_S_TUNER:
+               case VIDIOC_G_MODULATOR:
+               case VIDIOC_S_MODULATOR:
+               case VIDIOC_G_FREQUENCY:
+               case VIDIOC_S_FREQUENCY:
+               {
+                       /* we don't have a tuner or modulator */
+                       return -EINVAL;
+               }
+
+               case VIDIOC_ENUMOUTPUT:
+               case VIDIOC_G_OUTPUT:
+               case VIDIOC_S_OUTPUT:
+               {
+                       /* we don't have any video outputs */
+                       return -EINVAL;
+               }
+
+               default:
+               {
+                       /* unrecognized ioctl */
+                       return -ENOIOCTLCMD;
+               }
+       }
+       return 0;
+}
+
+/*
+ *  file operations
+ */
+
+static unsigned
+int camera_core_poll(struct file *file, struct poll_table_struct *wait)
+{
+       return -EINVAL;
+}
+
+/* ------------------------------------------------------------ */
+/* callback routine for read DMA completion. We just start another DMA
+ * transfer unless overlay has been turned off
+ */
+static void
+camera_core_capture_callback(void *arg1, void *arg)
+{
+       struct camera_device *cam = (struct camera_device *)arg1;
+       int err;
+       unsigned long irqflags;
+       static int done = 0;
+
+       spin_lock_irqsave(&cam->capture_lock, irqflags);
+       if (!cam->reading)
+       {
+               done = 0;
+               cam->capture_started = 0;
+               spin_unlock_irqrestore(&cam->capture_lock, irqflags);
+               return;
+       }
+
+       if (done < 14) {
+               ++done;
+               sg_dma_address(&cam->capture_sglist) = cam->capture_base_phys;
+               sg_dma_len(&cam->capture_sglist) = cam->pix.sizeimage;
+               err = camera_core_sgdma_queue(cam, &cam->capture_sglist, 1,
+                       camera_core_capture_callback, NULL);            
+       } else {
+               cam->capture_completed = 1;
+               if (cam->reading)
+               {
+                       /* Wake up any process which are waiting for the 
+                       ** DMA to complete */
+                       wake_up_interruptible(&camera_dev->new_video_frame);
+                       sg_dma_address(&cam->capture_sglist) = cam->capture_base_phys;
+                       sg_dma_len(&cam->capture_sglist) = cam->pix.sizeimage;
+                       err = camera_core_sgdma_queue(cam, &cam->capture_sglist, 1,
+                               camera_core_capture_callback, NULL);
+               }
+       }
+
+       spin_unlock_irqrestore(&cam->capture_lock, irqflags);
+}
+
+static ssize_t
+camera_core_read(struct file *file, char *data, size_t count, loff_t *ppos)
+{
+       struct camera_fh *fh = file->private_data;
+       struct camera_device *cam = fh->cam;
+       int err;
+       unsigned long irqflags;
+       long timeout;
+#if 0  /* use video_buf to do capture */
+       int i;
+       for (i = 0; i < 14; i++)
+               videobuf_read_one(file, &fh->vbq, data, count, ppos);
+       i = videobuf_read_one(file, &fh->vbq, data, count, ppos);
+       return i;
+#endif
+       
+       if (!cam->capture_base) {
+               cam->capture_base = (unsigned long)dma_alloc_coherent(NULL,
+                               cam->pix.sizeimage,
+                               (dma_addr_t *) &cam->capture_base_phys,
+                               GFP_KERNEL | GFP_DMA);
+       }
+       if (!cam->capture_base) {
+               printk(KERN_ERR CAM_NAME
+                       ": cannot allocate capture buffer\n");
+               return 0;
+       }
+
+       spin_lock_irqsave(&cam->capture_lock, irqflags);
+       cam->reading = fh;
+       cam->capture_started = 1;
+       sg_dma_address(&cam->capture_sglist) = cam->capture_base_phys;
+       sg_dma_len(&cam->capture_sglist)= cam->pix.sizeimage;
+       spin_unlock_irqrestore(&cam->capture_lock, irqflags);
+
+       err = camera_core_sgdma_queue(cam, &cam->capture_sglist, 1,
+                       camera_core_capture_callback, NULL);
+
+       /* Wait till DMA is completed */
+       timeout = HZ * 10;
+       cam->capture_completed = 0;
+       while (cam->capture_completed == 0) {
+               timeout = interruptible_sleep_on_timeout 
+                               (&cam->new_video_frame, timeout);
+               if (timeout == 0) {
+                       printk(KERN_ERR CAM_NAME ": timeout waiting video frame\n");    
+                       return -EIO; /* time out */
+               }
+       }
+       /* copy the data to the user buffer */
+       err = copy_to_user(data, (void *)cam->capture_base, cam->pix.sizeimage);
+       return (cam->pix.sizeimage - err);
+       
+}
+
+static int
+camera_core_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct camera_fh *fh = file->private_data;
+
+       return videobuf_mmap_mapper(&fh->vbq, vma);
+}
+
+static int
+camera_core_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 
+                 unsigned long arg)
+{
+
+       return video_usercopy(inode, file, cmd, arg, camera_core_do_ioctl);
+}
+
+static int
+camera_core_release(struct inode *inode, struct file *file)
+{
+       struct camera_fh *fh = file->private_data;
+       struct camera_device *cam = fh->cam;
+       
+       file->private_data = NULL;
+       kfree(fh);
+
+       spin_lock(&cam->img_lock);
+       if (cam->previewing == fh) {
+               cam->previewing = NULL;
+       }
+       if (cam->streaming == fh) {
+               cam->streaming = NULL;
+       }
+       if (cam->reading == fh) {
+               cam->reading = NULL;
+       }
+       spin_unlock(&cam->img_lock);
+
+       camera_dev->cam_hardware->finish_dma(cam->hardware_data);
+
+       if (cam->capture_base) {
+               dma_free_coherent(NULL, cam->pix.sizeimage, 
+                                       (void *)cam->capture_base, 
+                                       cam->capture_base_phys);
+               cam->capture_base = 0;
+               cam->capture_base_phys = 0;
+       }
+       if (fh->vbq.read_buf) {
+               camera_core_vbq_release(&fh->vbq, fh->vbq.read_buf);
+               kfree(fh->vbq.read_buf);
+       }
+
+       cam->cam_hardware->close(cam->hardware_data);
+       cam->active = 0;
+       return 0;
+}
+
+static int
+camera_core_open(struct inode *inode, struct file *file)
+{
+       int minor = iminor(inode);
+       struct camera_device *cam = camera_dev;
+       struct camera_fh *fh;
+
+       if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+               return -ENODEV;
+
+       /* allocate per-filehandle data */
+       fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+       if (NULL == fh)
+               return -ENOMEM;
+       file->private_data = fh;
+       fh->cam = cam;
+       fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       spin_lock(&cam->img_lock);
+       if (cam->active == 1) {
+               printk (KERN_ERR CAM_NAME ": Camera device Active\n");
+               spin_unlock(&cam->img_lock);
+               return -EPERM;
+       }
+       cam->active = 1;
+       spin_unlock(&cam->img_lock);
+
+       videobuf_queue_init(&fh->vbq, &cam->vbq_ops, NULL, &cam->vbq_lock,
+               fh->type, V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), fh);
+
+       cam->capture_completed = 0;
+       cam->capture_started = 0;
+
+       if (cam->cam_hardware->open(cam->hardware_data))
+       {
+               printk (KERN_ERR CAM_NAME ": Camera IF configuration failed\n");
+               cam->active = 0;
+               return -ENODEV;
+       }
+       
+       cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data);
+       /* program the sensor for the capture format and rate */
+       if (cam->cam_sensor->configure(&cam->pix, cam->xclk, 
+                               &cam->cparm.timeperframe, cam->sensor_data))
+       {
+               printk (KERN_ERR CAM_NAME ": Camera sensor configuration failed\n");
+               cam->cam_hardware->close(cam->hardware_data);
+               cam->active = 0;
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int camera_core_suspend(struct device *dev, u32 state, u32 level)
+{
+       struct camera_device *cam = dev_get_drvdata(dev);
+       int ret = 0;
+
+       spin_lock(&cam->img_lock);
+       switch (level) {
+       case SUSPEND_POWER_DOWN:
+               if (cam->active) {
+                       cam->cam_hardware->close(cam->hardware_data);
+               }
+               cam->cam_sensor->power_off(cam->sensor_data);
+               break;
+       }
+
+       spin_unlock(&cam->img_lock);
+       return ret;
+}
+
+static int camera_core_resume(struct device *dev, u32 level)
+{
+       struct camera_device *cam = dev_get_drvdata(dev);
+       int ret = 0;
+
+       spin_lock(&cam->img_lock);
+       switch (level) {
+       case RESUME_POWER_ON:
+               cam->cam_sensor->power_on(cam->sensor_data);
+               if (cam->active) {
+                       cam->capture_completed = 1;
+                       cam->cam_hardware->open(cam->hardware_data);
+                       cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data);
+
+                       cam->cam_sensor->configure(&cam->pix, cam->xclk,
+                               &cam->cparm.timeperframe, cam->sensor_data);
+
+                       camera_core_sgdma_process(cam);
+               }
+               break;
+       }
+
+       spin_unlock(&cam->img_lock);
+       return ret;
+}
+#endif /* CONFIG_PM */
+
+static struct file_operations camera_core_fops = 
+{
+       .owner                  = THIS_MODULE,
+       .llseek                 = no_llseek,
+       .read                   = camera_core_read,
+       .poll                   = camera_core_poll,
+       .ioctl                  = camera_core_ioctl,
+       .mmap                   = camera_core_mmap,
+       .open                   = camera_core_open,
+       .release                = camera_core_release,
+};
+
+static struct device_driver camera_core_driver = {
+       .name                   = CAM_NAME,
+       .bus                    = &platform_bus_type,
+       .probe                  = NULL,
+       .remove                 = NULL,
+#ifdef CONFIG_PM
+       .suspend                = camera_core_suspend,
+       .resume                 = camera_core_resume,
+#endif
+       .shutdown               = NULL,
+};
+
+static struct platform_device camera_core_device = {
+       .name   = CAM_NAME,
+       .dev    = {
+                       .release        = NULL,
+                 },
+       .id     = 0,
+};
+
+void
+camera_core_cleanup(void)
+{
+       struct camera_device *cam = camera_dev;
+       struct video_device *vfd;
+
+       if (!cam)
+               return;
+
+       vfd = cam->vfd;
+       if (vfd) {
+               if (vfd->minor == -1) {
+                       /* The device never got registered, so release the 
+                       ** video_device struct directly
+                       */
+                       video_device_release(vfd);
+               } else {
+                       /* The unregister function will release the video_device
+                       ** struct as well as unregistering it.
+                       */
+                       video_unregister_device(vfd);
+                       driver_unregister(&camera_core_driver);
+                       platform_device_unregister(&camera_core_device);
+               }
+               cam->vfd = NULL;
+       }
+       if (cam->overlay_base) {
+               dma_free_coherent(NULL, cam->overlay_size,
+                                       (void *)cam->overlay_base, 
+                                       cam->overlay_base_phys);
+               cam->overlay_base = 0;
+       }       
+       cam->overlay_base_phys = 0;
+
+       cam->cam_sensor->cleanup(cam->sensor_data);
+       cam->cam_hardware->cleanup(cam->hardware_data);
+       kfree(cam);
+       camera_dev = NULL;
+
+       return;
+}
+
+
+int __init 
+camera_core_init(void)
+{
+       struct camera_device *cam;
+       struct video_device *vfd;
+
+       cam = kmalloc(sizeof(struct camera_device), GFP_KERNEL);
+       if (!cam) {
+               printk(KERN_ERR CAM_NAME ": could not allocate memory\n");
+               goto init_error;
+       }
+       memset(cam, 0, sizeof(struct camera_device));
+       
+       /* Save the pointer to camera device in a global variable */
+       camera_dev = cam;
+
+       /* initialize the video_device struct */
+       vfd = cam->vfd = video_device_alloc();
+       if (!vfd) {
+               printk(KERN_ERR CAM_NAME 
+                       ": could not allocate video device struct\n");
+               goto init_error;
+       }
+       
+       vfd->release = video_device_release;
+
+       strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+       vfd->type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CHROMAKEY;
+       
+       /* need to register for a VID_HARDWARE_* ID in videodev.h */
+       vfd->hardware = 0;
+       vfd->fops = &camera_core_fops;
+       video_set_drvdata(vfd, cam);
+       vfd->minor = -1;
+
+       /* initialize the videobuf queue ops */
+       cam->vbq_ops.buf_setup = camera_core_vbq_setup;
+       cam->vbq_ops.buf_prepare = camera_core_vbq_prepare;
+       cam->vbq_ops.buf_queue = camera_core_vbq_queue;
+       cam->vbq_ops.buf_release = camera_core_vbq_release;
+
+       /* initilize the overlay interface */
+       cam->overlay_size = overlay_mem;
+       if (cam->overlay_size > 0)
+       {
+               cam->overlay_base = (unsigned long) dma_alloc_coherent(NULL,
+                                       cam->overlay_size,
+                                       (dma_addr_t *) &cam->overlay_base_phys,
+                                       GFP_KERNEL | GFP_DMA);
+               if (!cam->overlay_base) {
+                       printk(KERN_ERR CAM_NAME
+                               ": cannot allocate overlay framebuffer\n");
+                       goto init_error;
+               }
+       }
+       memset((void*)cam->overlay_base, 0, cam->overlay_size);
+       spin_lock_init(&cam->overlay_lock);
+       spin_lock_init(&cam->capture_lock);
+
+       /*Initialise the pointer to the sensor interface and camera interface */
+       cam->cam_sensor = &camera_sensor_if;
+       cam->cam_hardware = &camera_hardware_if;
+
+       /* initialize the camera interface */
+       cam->hardware_data = cam->cam_hardware->init();
+       if (!cam->hardware_data) {
+               printk(KERN_ERR CAM_NAME ": cannot initialize interface hardware\n");
+               goto init_error;
+       }
+        
+       /* initialize the spinlock used to serialize access to the image 
+        * parameters
+        */
+       spin_lock_init(&cam->img_lock);
+
+       /* initialize the streaming capture parameters */
+       cam->cparm.capability = V4L2_CAP_TIMEPERFRAME;
+       cam->cparm.readbuffers = 1;
+
+       /* Enable the xclk output.  The sensor may (and does, in the case of 
+        * the OV9640) require an xclk input in order for its initialization 
+        * routine to work.
+        */
+       cam->xclk = 21000000;   /* choose an arbitrary xclk frequency */
+       cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data);
+
+       /* initialize the sensor and define a default capture format cam->pix */
+       cam->sensor_data = cam->cam_sensor->init(&cam->pix);
+       if (!cam->sensor_data) {
+               cam->cam_hardware->disable(cam->hardware_data);
+               printk(KERN_ERR CAM_NAME ": cannot initialize sensor\n");
+               goto init_error;
+       }
+
+       printk(KERN_INFO CAM_NAME ": %s interface with %s sensor\n",
+               cam->cam_hardware->name, cam->cam_sensor->name);
+
+       /* select an arbitrary default capture frame rate of 15fps */
+       cam->nominal_timeperframe.numerator = 1;
+       cam->nominal_timeperframe.denominator = 15;
+
+       /* calculate xclk based on the default capture format and default 
+        * frame rate
+        */
+       cam->xclk = cam->cam_sensor->calc_xclk(&cam->pix,
+               &cam->nominal_timeperframe, cam->sensor_data);
+       cam->cparm.timeperframe = cam->nominal_timeperframe;
+
+       /* initialise the wait queue */
+       init_waitqueue_head(&cam->new_video_frame);
+
+       /* Initialise the DMA structures */
+       camera_core_sgdma_init(cam);
+
+       /* Disable the Camera after detection */
+       cam->cam_hardware->disable(cam->hardware_data);
+
+       dev_set_drvdata(&camera_core_device.dev, (void *)cam);
+       if (platform_device_register(&camera_core_device) < 0) {
+               printk(KERN_ERR CAM_NAME
+                       ": could not register platform_device\n");
+               goto init_error;
+       }
+
+       if (driver_register(&camera_core_driver) < 0) {
+               printk(KERN_ERR CAM_NAME
+                       ": could not register driver\n");
+               platform_device_unregister(&camera_core_device);
+               goto init_error;
+       }
+       if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+               printk(KERN_ERR CAM_NAME 
+                       ": could not register Video for Linux device\n");
+               platform_device_unregister(&camera_core_device);
+               driver_unregister(&camera_core_driver);
+               goto init_error;
+       }
+
+       printk(KERN_INFO CAM_NAME 
+               ": registered device video%d [v4l2]\n", vfd->minor);
+       return 0;
+
+init_error:
+       camera_core_cleanup();
+       return -ENODEV;
+}
+
+MODULE_AUTHOR("Texas Instruments.");
+MODULE_DESCRIPTION("OMAP Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, 
+               "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem,
+        "Maximum amount of memory for capture buffers (default 4800KB)");
+
+module_init(camera_core_init);
+module_exit(camera_core_cleanup);
+
+
diff --git a/drivers/media/video/omap/camera_core.h b/drivers/media/video/omap/camera_core.h
new file mode 100644 (file)
index 0000000..9ab336f
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *  drivers/media/video/omap/camera_core.h
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * 
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+
+#ifndef CAMERA_CORE__H
+#define CAMERA_CORE__H
+
+struct camera_fh;
+
+#include <media/video-buf.h>
+#include <asm/scatterlist.h>
+
+struct camera_device;
+typedef void (*dma_callback_t)(void *arg1, void *arg2);
+
+struct sgdma_state {
+       const struct scatterlist *sglist;
+       int sglen;              /* number of sglist entries */
+       int next_sglist;        /* index of next sglist entry to process */
+       int queued_sglist;      /* number of sglist entries queued for DMA */
+       unsigned long csr;      /* DMA return code */
+       dma_callback_t callback;
+       void *arg;
+};
+
+/* NUM_SG_DMA is the number of scatter-gather DMA transfers that can be queued.
+ */
+#define NUM_SG_DMA VIDEO_MAX_FRAME+2
+/* per-device data structure */
+struct camera_device {
+       struct device dev;
+       struct video_device *vfd;
+       
+       spinlock_t overlay_lock;        /* spinlock for overlay DMA counter */
+       int overlay_cnt;                /* count of queued overlay DMA xfers */
+       struct scatterlist overlay_sglist;
+       unsigned long overlay_base_phys;
+       unsigned long overlay_base;
+       unsigned long overlay_size;
+
+       spinlock_t vbq_lock;            /* spinlock for videobuf queues */
+       struct videobuf_queue_ops vbq_ops;      /* videobuf queue operations */
+       unsigned long field_count;      /* field counter for videobuf_buffer */
+
+       /* scatter-gather DMA management */
+       spinlock_t sg_lock;
+       int free_sgdma; /* number of free sg dma slots */
+       int next_sgdma; /* index of next sg dma slot to use */
+       struct sgdma_state sgdma[NUM_SG_DMA];
+       char in_use;
+
+       /* The img_lock is used to serialize access to the image parameters for 
+        * overlay and capture.  Need to use spin_lock_irq when writing to the 
+        * reading, streaming, and previewing parameters.  A regular spin_lock 
+        * will suffice for all other cases.
+        */
+       spinlock_t img_lock;
+       /* We allow reading from at most one filehandle at a time.
+        * non-NULL means reading is in progress.
+        */
+       struct camera_fh *reading;
+       /* We allow streaming from at most one filehandle at a time.  
+        * non-NULL means streaming is in progress.
+        */
+       struct camera_fh *streaming;
+       /* We allow previewing from at most one filehandle at a time.  
+        * non-NULL means previewing is in progress.
+        */
+       struct camera_fh *previewing;
+
+       /* capture parameters (frame rate, number of buffers) */
+       struct v4l2_captureparm cparm;
+
+       /* This is the frame period actually requested by the user. */
+       struct v4l2_fract nominal_timeperframe;
+       
+       /* frequency (in Hz) of camera interface xclk output */
+       unsigned long xclk;
+
+       /* Pointer to the sensor interface ops */
+       struct camera_sensor *cam_sensor;
+       void *sensor_data;
+       
+       /* Pointer to the camera interface hardware ops */
+       struct camera_hardware *cam_hardware;
+       void *hardware_data;
+
+       /* pix defines the size and pixel format of the image captured by the 
+        * sensor.  This also defines the size of the framebuffers.  The 
+        * same pool of framebuffers is used for video capture and video 
+        * overlay.  These parameters are set/queried by the 
+        * VIDIOC_S_FMT/VIDIOC_G_FMT ioctls with a CAPTURE buffer type.
+        */
+       struct v4l2_pix_format pix;
+
+       /* crop defines the size and offset of the video overlay source window 
+        * within the framebuffer.  These parameters are set/queried by the 
+        * VIDIOC_S_CROP/VIDIOC_G_CROP ioctls with an OVERLAY buffer type.  
+        * The cropping rectangle allows a subset of the captured image to be 
+        * previewed.  It only affects the portion of the image previewed, not 
+        * captured; the entire camera image is always captured.
+        */
+       struct v4l2_rect crop;
+
+       /* win defines the size and offset of the video overlay target window 
+        * within the video display.  These parameters are set/queried by the 
+        * VIDIOC_S_FMT/VIDIOC_G_FMT ioctls with an OVERLAY buffer type.
+        */
+       struct v4l2_window win;
+
+       /* fbuf reflects the size of the video display.  It is queried with the 
+        * VIDIOC_G_FBUF ioctl.  The size of the video display cannot be 
+        * changed with the VIDIOC_S_FBUF ioctl.
+        */
+       struct v4l2_framebuffer fbuf;
+
+       /* end of generic stuff, the above should be common to all omaps */
+
+       /* note, 2420 uses videobuf to do caprure, it is more memory efficient
+          we need 1710 and 2420 do capture in the same way */
+       /* Variables to store the capture state */
+       /* Wait till DMA is completed */
+       wait_queue_head_t new_video_frame;
+       char capture_completed;
+       char capture_started;
+       spinlock_t capture_lock;
+       struct scatterlist capture_sglist;
+       unsigned long capture_base;
+       unsigned long capture_base_phys;
+
+       char active;
+};
+
+/* per-filehandle data structure */
+struct camera_fh {
+       struct camera_device *cam;
+       enum v4l2_buf_type type;
+       struct videobuf_queue vbq;
+};
+
+#define CAM_NAME "omap-camera"
+
+#endif /* CAMERA_CORE__H */
diff --git a/drivers/media/video/omap/camera_hw_if.h b/drivers/media/video/omap/camera_hw_if.h
new file mode 100644 (file)
index 0000000..2c941b5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  drivers/media/video/omap/camera_hw_if.h
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * 
+ * Camera interface to OMAP camera capture drivers
+ * Camera interface hardware driver should implement this interface
+ *
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+#ifndef OMAP_CAMERA_HW_IF_H
+#define OMAP_CAMERA_HW_IF_H
+
+#define LEN_HW_IF_NAME         31
+
+struct sgdma_state;
+
+struct camera_hardware {
+       unsigned int version;  //version of camera driver module
+       char name[LEN_HW_IF_NAME + 1];
+
+       void *(*init)(void);
+       int (*cleanup)(void *);
+
+       int (*open)(void *);  /* acquire h/w resources (irq,DMA), etc. */
+       int (*close)(void *); /* free h/w resources, stop i/f */
+
+       int (*enable)(void *);
+       int (*disable)(void *);
+
+       int (*abort)(void *);
+
+       int (*set_xclk)(int, void *);
+
+       int (*init_dma)(void *);
+       int (*start_dma)(struct sgdma_state *, void (*)(void *arg1, void *arg2),
+                       void *, void *, void *);
+       int (*finish_dma)(void *);
+};
+#endif /* OMAP_CAMERA_HW_IF_H */
diff --git a/drivers/media/video/omap/omap16xxcam.c b/drivers/media/video/omap/omap16xxcam.c
new file mode 100644 (file)
index 0000000..806c4a8
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+ * drivers/media/video/omap/omap16xxcam.c
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * 
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP H2 and H3 camera controller.
+ *
+ * leverage some code from CEE distribution 
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ * 
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+#include <linux/config.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <asm/arch/irqs.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/clock.h>
+
+#include "omap16xxcam.h"
+#include "camera_hw_if.h"
+#include "camera_core.h"
+  
+#define CONF_CAMERAIF_RESET_R 5
+#define EN_PER   0
+
+/* NUM_CAMDMA_CHANNELS is the number of logical channels used for
+ * DMA data transfer.
+ */
+#define NUM_CAMDMA_CHANNELS 2
+
+typedef struct {
+        unsigned int ctrlclock;     /* 00 */
+        unsigned int it_status;     /* 04 */
+        unsigned int mode;          /* 08 */
+        unsigned int status;        /* 0C */
+        unsigned int camdata;       /* 10 */
+        unsigned int gpio;         /* 14 */
+        unsigned int peak_counter;  /* 18 */
+} camera_regs_t;
+
+struct camdma_state {
+       dma_callback_t callback;
+       void *arg1;
+       void *arg2;
+};
+
+struct omap16xxcam {
+       camera_regs_t *camera_regs;
+       unsigned long iobase_phys;
+
+       /* frequncy (in Hz) of camera interface functional clock (ocp_clk) */
+       unsigned long ocp_clk; 
+
+       /* dma related stuff */
+       spinlock_t dma_lock;
+       int free_dmach;
+       int next_dmach;
+       struct camdma_state camdma[NUM_CAMDMA_CHANNELS];
+       int dma_channel_number1;
+       int dma_channel_number2;
+
+       wait_queue_head_t vsync_wait;
+       
+       int new;
+};
+static struct omap16xxcam hardware_data;
+   
+static int omap16xxcam_set_xclk(int, void *);
+static void omap16xx_cam_dma_link_callback(int, unsigned short, void *);
+
+/* Clears the camera data FIFO by setting RAZ_FIFO bit in MODE configuration
+   register. */
+static void
+omap16xx_cam_clear_fifo(struct omap16xxcam *data)
+{
+       data->camera_regs->mode |= RAZ_FIFO;
+       udelay(10);
+       data->camera_regs->mode &= ~RAZ_FIFO;
+}
+  
+static void
+omap16xx_cam_reset(struct omap16xxcam *data, int yes)
+{
+       if (machine_is_omap_h3())
+               data->camera_regs->gpio = yes ? 0 : 1;
+       else
+               data->camera_regs->gpio = yes ? 1 : 0;
+}
+
+static void 
+omap16xx_cam_init(void)
+{
+       /*
+        * FIXME - Use mux API's instead of directly writing in to MUX registers
+        */
+       omap_writel(omap_readl(FUNC_MUX_CTRL_4) & ~(0x1ff << 21), FUNC_MUX_CTRL_4);
+       omap_writel(0, FUNC_MUX_CTRL_5);
+       omap_writel(omap_readl(PULL_DWN_CTRL_0) & ~(0x1FFF << 17), PULL_DWN_CTRL_0);
+       omap_writel(omap_readl(PU_PD_SEL_0) & ~(0x1FFF << 17), PU_PD_SEL_0);
+
+       omap_writel(0xeaef, COMP_MODE_CTRL_0);
+       omap_writel(omap_readl(OMAP1610_RESET_CONTROL) & ~(1 << CONF_CAMERAIF_RESET_R),
+                       OMAP1610_RESET_CONTROL);
+       omap_writel(omap_readl(OMAP1610_RESET_CONTROL) | (1 << CONF_CAMERAIF_RESET_R),
+                       OMAP1610_RESET_CONTROL);
+    
+       /* Enable peripheral reset */
+       omap_writew(omap_readw(ARM_RSTCT2) | (1 << EN_PER), ARM_RSTCT2); 
+
+       /* enable peripheral clock */
+       if (machine_is_omap_h3())
+               clk_enable(clk_get(0, "tc2_ck"));
+       else {
+               clk_enable(clk_get(0, "armper_ck"));
+               clk_enable(clk_get(0, "armxor_ck"));
+       }               
+}
+
+static void
+omap16xx_cam_waitfor_syncedge(struct omap16xxcam *data, u32 edge_mask)
+{
+       data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT) | edge_mask;
+       do {
+               interruptible_sleep_on(&data->vsync_wait);
+       } while (signal_pending(current));
+}
+
+static void
+omap16xx_cam_configure_dma(struct omap16xxcam *data)
+{
+
+       data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT)
+                        | EN_DMA | EN_FIFO_FULL;
+       data->camera_regs->ctrlclock |= LCLK_EN; 
+}
+
+/* acquire h/w resources DMA */
+static int
+omap16xx_cam_link_open(struct omap16xxcam *data)
+{
+       int ret;
+
+       /* Acquire first dma channel */
+       if ((ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, 
+                               "camera dma 1", omap16xx_cam_dma_link_callback,
+                               (void *)data, &data->dma_channel_number1))) {
+                return ret;
+       }
+       /* Acquire second dma channel */
+       if ((ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, 
+                               "camera dma 2", omap16xx_cam_dma_link_callback,
+                               (void *)data, &data->dma_channel_number2))) {
+                printk ("No DMA available for camera\n");
+                return ret;
+        }
+       data->next_dmach = data->dma_channel_number1;
+       omap_writew(data->dma_channel_number2, 
+                               OMAP_DMA_CLNK_CTRL(data->dma_channel_number1));
+       omap_writew(data->dma_channel_number1, 
+                               OMAP_DMA_CLNK_CTRL(data->dma_channel_number2));
+
+       return 0;
+}
+
+/* free h/w resources, stop i/f */
+static int
+omap16xx_cam_link_close(struct omap16xxcam *data)
+{
+       /* free dma channels */
+       omap_stop_dma(data->dma_channel_number1);
+       omap_stop_dma(data->dma_channel_number2);
+
+       omap_free_dma (data->dma_channel_number1);
+       omap_free_dma (data->dma_channel_number2);
+
+       return 0;
+}
+
+/* dma callback routine. */
+static void
+omap16xx_cam_dma_link_callback(int lch, unsigned short ch_status, void *data)
+{
+       int count;
+       void *arg1, *arg2;
+       struct sgdma_state *sgdma = sgdma;
+       struct omap16xxcam *cam = (struct omap16xxcam *)data;
+       dma_callback_t callback;
+
+       spin_lock(&cam->dma_lock);
+       if (cam->free_dmach == 2)
+       {
+               printk("callback all CHANNELS WERE IDLE \n");
+               spin_unlock(&cam->dma_lock);
+               return;
+       }
+       if (cam->free_dmach == 0) {
+               lch = cam->next_dmach;
+       } else {
+               lch = cam->next_dmach == cam->dma_channel_number1 ? 
+                       cam->dma_channel_number2 : cam->dma_channel_number1;
+       }
+
+       while (cam->free_dmach < 2)
+       {
+               if ((omap_readw(OMAP_DMA_CCR(lch)) & (1 << 7) ))
+                       break;  
+
+               count = (lch == cam->dma_channel_number2) ? 1 : 0;
+
+               callback = cam->camdma[count].callback;
+               arg1 = cam->camdma[count].arg1;
+               arg2 = cam->camdma[count].arg2;
+               cam->free_dmach++;
+
+               spin_unlock(&cam->dma_lock);            
+               callback(arg1, arg2);
+               spin_lock(&cam->dma_lock);
+
+               lch = (lch == cam->dma_channel_number2) ? cam->dma_channel_number1 :
+                                                       cam->dma_channel_number2;
+       }
+       spin_unlock(&cam->dma_lock);
+       
+}
+
+static irqreturn_t
+omap16xx_cam_isr(int irq, void *client_data, struct pt_regs *regs)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *)client_data;
+       unsigned int itstat = data->camera_regs->it_status;
+
+       /* VSYNC UP interrupt, start filling FIFO and enabling DMA */
+       if (itstat & V_UP) {            
+               data->camera_regs->mode &= ~EN_V_UP;
+               omap16xx_cam_clear_fifo(data);  
+               omap16xx_cam_configure_dma(data);
+               omap_start_dma(data->next_dmach);
+               wake_up_interruptible(&data->vsync_wait);
+       }
+
+       if (itstat & V_DOWN) {
+               data->camera_regs->mode &= ~EN_V_DOWN;
+               wake_up_interruptible(&data->vsync_wait);
+       }
+
+       if (itstat & H_UP)
+               printk("H_UP\n");
+       
+       if (itstat & H_DOWN)
+               printk("H_DOWN\n");
+       
+       if (itstat & FIFO_FULL) {
+               omap16xx_cam_clear_fifo(data);  
+               printk("FIFO_FULL\n");
+       }
+       
+       if (itstat & DATA_XFER)
+               printk("DATA_TRANS\n");
+       
+       return IRQ_HANDLED;
+}
+/* ------------- below are interface functions ----------------- */
+/* ------------- these functions are named omap16xxcam_<name> -- */
+static int
+omap16xxcam_init_dma(void *priv)
+{
+       int ch;
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+
+       data->free_dmach = 2;
+       for (ch = 0; ch < 2; ++ch) {
+               data->camdma[ch].callback = NULL;
+               data->camdma[ch].arg1 = NULL;
+               data->camdma[ch].arg2 = NULL;
+       }
+
+       return 0;
+}
+
+/* start the dma of chains */
+static int 
+omap16xxcam_start_dma(struct sgdma_state *sgdma,
+               dma_callback_t callback, void *arg1, void *arg2, void *priv)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+       struct scatterlist *sglist;
+       unsigned long irqflags;
+       int dmach;
+       int prev_dmach;
+       int count;
+
+       spin_lock_irqsave(&data->dma_lock, irqflags);
+       sglist = (struct scatterlist *)(sgdma->sglist + sgdma->next_sglist);
+
+       if (!data->free_dmach) {
+               spin_unlock_irqrestore(&data->dma_lock, irqflags);
+               return -EBUSY;
+       } 
+       dmach = data->next_dmach;
+       count = (dmach == data->dma_channel_number2) ? 1:0;
+       data->camdma[count].callback = callback;
+       data->camdma[count].arg1 = arg1;
+       data->camdma[count].arg2 = arg2;
+       
+       if (machine_is_omap_h3())
+               omap_set_dma_src_params(dmach, OMAP_DMA_PORT_OCP_T1,
+                           OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG);
+       else
+               omap_set_dma_src_params(dmach, OMAP_DMA_PORT_TIPB,
+                           OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG);
+
+       omap_set_dma_dest_params(dmach, OMAP_DMA_PORT_EMIFF,
+                            OMAP_DMA_AMODE_POST_INC, sg_dma_address(sglist));
+
+       omap_set_dma_transfer_params(dmach, OMAP_DMA_DATA_TYPE_S32,
+                       FIFO_TRIGGER_LVL, 
+                       sg_dma_len(sglist)/(4 * FIFO_TRIGGER_LVL), 
+                       OMAP_DMA_SYNC_FRAME);
+       
+
+       omap_writew(omap_readw(OMAP_DMA_CLNK_CTRL(dmach)) & ~(1<<15), 
+                                       OMAP_DMA_CLNK_CTRL(dmach));
+
+       prev_dmach = (dmach == data->dma_channel_number2) ? 
+               data->dma_channel_number1 : data->dma_channel_number2;
+       
+       if (data->new) {
+               data->new = 0;
+               omap16xx_cam_waitfor_syncedge(data, EN_V_UP);
+       } else {
+               if (omap_readw(OMAP_DMA_CCR(prev_dmach)) & (1 << 7)) {
+                       omap_writew((omap_readw(OMAP_DMA_CLNK_CTRL(prev_dmach)) |
+                                        (1 << 15)),
+                       OMAP_DMA_CLNK_CTRL(prev_dmach));
+               }
+               else {
+                       /* no transfer is in progress */
+                       omap_start_dma(dmach);
+               }       
+       }
+       
+       data->next_dmach = prev_dmach; 
+       data->free_dmach--;
+       spin_unlock_irqrestore(&data->dma_lock, irqflags);
+       return 0;
+}
+int static
+omap16xxcam_finish_dma(void *priv)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+
+       while (data->free_dmach < 2)
+               mdelay(1);
+
+       return 0;
+}
+
+
+/* Enables the camera. Takes camera out of reset. Enables the clocks. */ 
+static int
+omap16xxcam_enable(void *priv)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+
+       omap16xx_cam_reset(data, 1);
+       
+       /* give clock to camera_module */
+       data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT);
+       data->camera_regs->ctrlclock = MCLK_EN | CAMEXCLK_EN;
+
+       omap16xx_cam_clear_fifo(data);
+
+       /* wait for camera to settle down */
+       mdelay(5);
+
+       return 0;
+}
+/* Disables all the camera clocks. Put the camera interface in reset. */
+static int
+omap16xxcam_disable(void *priv)
+{      
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+
+       omap16xx_cam_clear_fifo(data);
+
+       data->camera_regs->ctrlclock = 0x00000000;     
+       data->camera_regs->mode = 0x00000000;
+       
+       omap16xx_cam_reset(data, 0);
+
+       return 0;
+}
+
+/* Abort the data transfer */
+static int
+omap16xxcam_abort(void *priv)
+{
+       return omap16xxcam_disable(priv);
+}
+
+static int
+omap16xxcam_set_xclk(int xclk, void *priv)
+{      
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+       int xclk_val;
+       int divisor = 1;
+       divisor = data->ocp_clk/xclk;
+       if ( divisor * xclk < data->ocp_clk)
+               ++divisor; 
+
+       switch (divisor) {
+               case 1:
+               case 2:
+                       xclk_val = FOSCMOD_TC2_CK2;
+                       break;
+               case 3:
+                       xclk_val = FOSCMOD_TC2_CK3;
+                       break;
+               case 4:
+               case 5:
+               case 6:
+               case 7:
+                       xclk_val = FOSCMOD_TC2_CK4;
+                       break;
+               case 8:
+               case 9:
+                       xclk_val = FOSCMOD_TC2_CK8;
+                       break;
+               case 10:
+               case 11:
+                       xclk_val = FOSCMOD_TC2_CK10;
+                       break;
+               case 12:
+               case 13:
+               case 14:
+               case 15:
+                       xclk_val = FOSCMOD_TC2_CK12;
+                       break;
+               case 16:
+                       xclk_val = FOSCMOD_TC2_CK16;
+                       break;
+               default:
+                       xclk_val = FOSCMOD_TC2_CK16;
+       }
+       
+       /* follow the protocol to change the XCLK clock */
+       data->camera_regs->ctrlclock &= ~CAMEXCLK_EN;
+       data->camera_regs->ctrlclock |= xclk_val;
+       data->camera_regs->ctrlclock |= CAMEXCLK_EN;
+
+       return (data->ocp_clk/divisor);
+}
+
+static int
+omap16xxcam_open(void *priv)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+       int ret;
+
+       if ((ret = request_irq(INT_CAMERA, omap16xx_cam_isr, SA_INTERRUPT, 
+                                       "camera", data))) {
+               printk("FAILED to aquire irq\n");
+               return ret;
+       }
+
+       data->new = 1;
+       omap16xxcam_enable(data);
+       omap16xxcam_init_dma(data);
+
+       return omap16xx_cam_link_open(data);
+}
+
+static int
+omap16xxcam_close(void *priv)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+
+       omap16xxcam_disable(priv);
+        
+       free_irq(INT_CAMERA, data);
+       
+       return omap16xx_cam_link_close(data);
+}
+
+static int
+omap16xxcam_cleanup(void *priv)
+{
+       struct omap16xxcam *data = (struct omap16xxcam *) priv;
+
+       omap16xxcam_disable(data);
+       if (machine_is_omap_h3()) {
+               if (data->camera_regs) {        
+                       iounmap((void *)data->camera_regs);
+                       data->camera_regs= NULL;
+               }
+       }
+
+       if (data->iobase_phys) {
+               release_mem_region(data->iobase_phys, CAMERA_IOSIZE);
+               data->iobase_phys = 0;
+       }
+
+       return 0;
+}
+
+/* Initialise the OMAP camera interface */
+static void *
+omap16xxcam_init(void)
+{
+       unsigned long cam_iobase;
+
+       if (!request_region(CAMERA_BASE, CAMERA_IOSIZE, "OAMP16xx Camera")) {
+               printk ("OMAP16XX Parallel Camera Interface is already in use\n");
+               return NULL;
+       }
+
+       if (machine_is_omap_h3()) {
+               cam_iobase = (unsigned long) ioremap (CAMERA_BASE, CAMERA_IOSIZE);
+               if (!cam_iobase) {
+                       printk("CANNOT MAP CAMERA REGISTER\n");
+                       return NULL;
+               }
+       }
+       else
+               cam_iobase = io_p2v(CAMERA_BASE);
+
+       /* Set the base address of the camera registers */
+       hardware_data.camera_regs = (camera_regs_t *)cam_iobase;
+       hardware_data.iobase_phys = (unsigned long) CAMERA_BASE;
+       /* get the input clock value to camera interface and store it */
+       if (machine_is_omap_h3())
+               hardware_data.ocp_clk = clk_get_rate(clk_get(0, "tc_ck"));
+       else
+               hardware_data.ocp_clk = clk_get_rate(clk_get(0, "mpuper_ck"));
+
+       /* Init the camera IF */
+       omap16xx_cam_init();
+       /* enable it. This is needed for sensor detection */
+       omap16xxcam_enable((void*)&hardware_data);
+       /* Init dma data */
+       spin_lock_init(&hardware_data.dma_lock);
+
+       init_waitqueue_head(&hardware_data.vsync_wait);
+       return (void*)&hardware_data;
+}
+
+struct camera_hardware camera_hardware_if = {
+       version         : 0x01, 
+       name            : "OMAP16xx Camera Parallel", 
+       init            : omap16xxcam_init,
+       cleanup         : omap16xxcam_cleanup,
+       open            : omap16xxcam_open, 
+       close           : omap16xxcam_close,
+       enable          : omap16xxcam_enable, 
+       disable         : omap16xxcam_disable,
+       abort           : omap16xxcam_abort,
+       set_xclk        : omap16xxcam_set_xclk,
+       init_dma        : omap16xxcam_init_dma,
+       start_dma       : omap16xxcam_start_dma,
+       finish_dma      : omap16xxcam_finish_dma,
+};
+
diff --git a/drivers/media/video/omap/omap16xxcam.h b/drivers/media/video/omap/omap16xxcam.h
new file mode 100644 (file)
index 0000000..14de5b7
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  drivers/media/video/omap/omap16xxcam.h
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * 
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+#ifndef OMAP_16XX_CAM_H
+#define OMAP_16XX_CAM_H
+
+#define DMA_ELEM_SIZE   4
+#define FIFO_TRIGGER_LVL (32)
+
+/*
+ * ---------------------------------------------------------------------------
+ *  OMAP1610 Camera Interface
+ * ---------------------------------------------------------------------------
+ */
+
+#ifdef CONFIG_MACH_OMAP_H3
+#define CAMERA_BASE          (0x2007d800)
+#else
+#define CAMERA_BASE          (IO_PHYS + 0x6800)
+#endif
+
+#define CAM_CTRLCLOCK_REG    (CAMERA_BASE + 0x00)
+#define CAM_IT_STATUS_REG    (CAMERA_BASE + 0x04)
+#define CAM_MODE_REG         (CAMERA_BASE + 0x08)
+#define CAM_STATUS_REG       (CAMERA_BASE + 0x0C)
+#define CAM_CAMDATA_REG      (CAMERA_BASE + 0x10)
+#define CAM_GPIO_REG         (CAMERA_BASE + 0x14)
+#define CAM_PEAK_CTR_REG     (CAMERA_BASE + 0x18)
+#define CAMERA_IOSIZE        0x1C
+
+/* CTRLCLOCK bit shifts */
+#define FOSCMOD_BIT                    0
+#define FOSCMOD_MASK                   (0x7 << FOSCMOD_BIT)
+#define FOSCMOD_12MHz          0x0
+#define        FOSCMOD_6MHz            0x2
+#define        FOSCMOD_9_6MHz          0x4
+#define        FOSCMOD_24MHz           0x5
+#define        FOSCMOD_8MHz            0x6
+#define        FOSCMOD_TC2_CK2         0x3
+#define        FOSCMOD_TC2_CK3         0x1
+#define        FOSCMOD_TC2_CK4         0x5
+#define        FOSCMOD_TC2_CK8         0x0
+#define        FOSCMOD_TC2_CK10        0x4
+#define        FOSCMOD_TC2_CK12        0x6
+#define        FOSCMOD_TC2_CK16        0x2
+#define        POLCLK                  (1<<3)
+#define        CAMEXCLK_EN             (1<<4)
+#define        MCLK_EN                 (1<<5)
+#define        DPLL_EN                 (1<<6)
+#define        LCLK_EN                 (1<<7)
+
+/* IT_STATUS bit shifts */
+#define V_UP           (1<<0)
+#define V_DOWN         (1<<1)
+#define H_UP           (1<<2)
+#define H_DOWN         (1<<3)
+#define FIFO_FULL      (1<<4)
+#define DATA_XFER      (1<<5)
+
+/* MODE bit shifts */
+#define CAMOSC         (1<<0)
+#define IMGSIZE_BIT    1
+#define IMGSIZE_MASK   (0x3 << IMGSIZE_BIT)
+#define        IMGSIZE_CIF      (0x0 << IMGSIZE_BIT)    /* 352x288 */
+#define        IMGSIZE_QCIF     (0x1 << IMGSIZE_BIT)    /* 176x144 */
+#define        IMGSIZE_VGA      (0x2 << IMGSIZE_BIT)    /* 640x480 */
+#define        IMGSIZE_QVGA     (0x3 << IMGSIZE_BIT)    /* 320x240 */
+#define ORDERCAMD      (1<<3)
+#define EN_V_UP        (1<<4)
+#define EN_V_DOWN      (1<<5)
+#define EN_H_UP        (1<<6)
+#define EN_H_DOWN      (1<<7)
+#define EN_DMA         (1<<8)
+#define THRESHOLD      (1<<9)
+#define THRESHOLD_BIT  9
+#define THRESHOLD_MASK (0x7f<<9)
+#define EN_NIRQ        (1<<16)
+#define EN_FIFO_FULL   (1<<17)
+#define RAZ_FIFO       (1<<18)
+
+/* STATUS bit shifts */
+#define VSTATUS        (1<<0)
+#define HSTATUS        (1<<1)
+
+/* GPIO bit shifts */
+#define CAM_RST        (1<<0)
+
+
+#define XCLK_6MHZ     6000000
+#define XCLK_8MHZ     8000000
+#define XCLK_9_6MHZ   9000000
+#define XCLK_12MHZ   12000000
+#define XCLK_24MHZ   24000000
+
+#endif /* OMAP_16XX_CAM_H */
diff --git a/drivers/media/video/omap/ov9640.h b/drivers/media/video/omap/ov9640.h
new file mode 100644 (file)
index 0000000..4cdba05
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * drivers/media/video/omap/ov9640.h
+ *
+ * Register definitions for the OmniVision OV9640 CameraChip.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License 
+ * version 2. This program is licensed "as is" without any warranty of any 
+ * kind, whether express or implied.
+ */
+
+#ifndef OV9640_H
+#define OV9640_H
+
+/* The OV9640 I2C sensor chip has a fixed slave address of 0x30. */
+#ifdef CONFIG_OMAP24XX_VIRTIO
+#define OV9640_I2C_ADDR                0x60
+#else
+#define OV9640_I2C_ADDR                0x30
+#endif
+
+/* define register offsets for the OV9640 sensor chip */
+#define OV9640_GAIN            0x00
+#define OV9640_BLUE            0x01
+#define OV9640_RED             0x02
+#define OV9640_VREF            0x03
+#define OV9640_COM1            0x04
+#define OV9640_BAVE            0x05
+#define OV9640_GEAVE           0x06
+#define OV9640_RAVE            0x08
+#define OV9640_COM2            0x09
+#define OV9640_PID             0x0A
+#define OV9640_VER             0x0B
+#define OV9640_COM3            0x0C
+#define OV9640_COM4            0x0D
+#define OV9640_COM5            0x0E
+#define OV9640_COM6            0x0F
+#define OV9640_AECH            0x10
+#define OV9640_CLKRC           0x11
+#define OV9640_COM7            0x12
+#define OV9640_COM8            0x13
+#define OV9640_COM9            0x14
+#define OV9640_COM10           0x15
+#define OV9640_HSTRT           0x17
+#define OV9640_HSTOP           0x18
+#define OV9640_VSTRT           0x19
+#define OV9640_VSTOP           0x1A
+#define OV9640_PSHFT           0x1B
+#define OV9640_MIDH            0x1C
+#define OV9640_MIDL            0x1D
+#define OV9640_MVFP            0x1E
+#define OV9640_LAEC            0x1F
+#define OV9640_BOS             0x20
+#define OV9640_GBOS            0x21
+#define OV9640_GROS            0x22
+#define OV9640_ROS             0x23
+#define OV9640_AEW             0x24
+#define OV9640_AEB             0x25
+#define OV9640_VPT             0x26
+#define OV9640_BBIAS           0x27
+#define OV9640_GBBIAS          0x28
+#define OV9640_EXHCH           0x2A
+#define OV9640_EXHCL           0x2B
+#define OV9640_RBIAS           0x2C
+#define OV9640_ADVFL           0x2D
+#define OV9640_ADVFH           0x2E
+#define OV9640_YAVE            0x2F
+#define OV9640_HSYST           0x30
+#define OV9640_HSYEN           0x31
+#define OV9640_HREF            0x32
+#define OV9640_CHLF            0x33
+#define OV9640_ARBLM           0x34
+#define OV9640_ADC             0x37
+#define OV9640_ACOM            0x38
+#define OV9640_OFON            0x39
+#define OV9640_TSLB            0x3A
+#define OV9640_COM11           0x3B
+#define OV9640_COM12           0x3C
+#define OV9640_COM13           0x3D
+#define OV9640_COM14           0x3E
+#define OV9640_EDGE            0x3F
+#define OV9640_COM15           0x40
+#define OV9640_COM16           0x41
+#define OV9640_COM17           0x42
+#define OV9640_MTX1            0x4F
+#define OV9640_MTX2            0x50
+#define OV9640_MTX3            0x51
+#define OV9640_MTX4            0x52
+#define OV9640_MTX5            0x53
+#define OV9640_MTX6            0x54
+#define OV9640_MTX7            0x55
+#define OV9640_MTX8            0x56
+#define OV9640_MTX9            0x57
+#define OV9640_MTXS            0x58
+#define OV9640_LCC1            0x62
+#define OV9640_LCC2            0x63
+#define OV9640_LCC3            0x64
+#define OV9640_LCC4            0x65
+#define OV9640_LCC5            0x66
+#define OV9640_MANU            0x67
+#define OV9640_MANV            0x68
+#define OV9640_HV              0x69
+#define OV9640_MBD             0x6A
+#define OV9640_DBLV            0x6B
+#define OV9640_GSP1            0x6C
+#define OV9640_GSP2            0x6D
+#define OV9640_GSP3            0x6E
+#define OV9640_GSP4            0x6F
+#define OV9640_GSP5            0x70
+#define OV9640_GSP6            0x71
+#define OV9640_GSP7            0x72
+#define OV9640_GSP8            0x73
+#define OV9640_GSP9            0x74
+#define OV9640_GSP10           0x75
+#define OV9640_GSP11           0x76
+#define OV9640_GSP12           0x77
+#define OV9640_GSP13           0x78
+#define OV9640_GSP14           0x79
+#define OV9640_GSP15           0x7A
+#define OV9640_GSP16           0x7B
+#define OV9640_GST1            0x7C
+#define OV9640_GST2            0x7D
+#define OV9640_GST3            0x7E
+#define OV9640_GST4            0x7F
+#define OV9640_GST5            0x80
+#define OV9640_GST6            0x81
+#define OV9640_GST7            0x82
+#define OV9640_GST8            0x83
+#define OV9640_GST9            0x84
+#define OV9640_GST10           0x85
+#define OV9640_GST11           0x86
+#define OV9640_GST12           0x87
+#define OV9640_GST13           0x88
+#define OV9640_GST14           0x89
+#define OV9640_GST15           0x8A
+
+#define OV9640_NUM_REGS                (OV9640_GST15 + 1)
+
+#define OV9640_PID_MAGIC       0x96    /* high byte of product ID number */
+#define OV9640_VER_REV2                0x48    /* low byte of product ID number */
+#define OV9640_VER_REV3                0x49    /* low byte of product ID number */
+#define OV9640_MIDH_MAGIC      0x7F    /* high byte of mfg ID */
+#define OV9640_MIDL_MAGIC      0xA2    /* low byte of mfg ID */
+
+/* define a structure for ov9640 register initialization values */
+struct ov9640_reg {
+       unsigned char reg;
+       unsigned char val;
+};
+
+enum image_size { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+enum pixel_format { YUV, RGB565, RGB555 };
+#define NUM_IMAGE_SIZES 7
+#define NUM_PIXEL_FORMATS 3
+
+struct capture_size {
+       unsigned long width;
+       unsigned long height;
+};
+
+/* Array of image sizes supported by OV9640.  These must be ordered from 
+ * smallest image size to largest.
+ */
+const static struct capture_size ov9640_sizes[] = {
+       {   88,  72 },  /* QQCIF */
+       {  160, 120 },  /* QQVGA */
+       {  176, 144 },  /* QCIF */
+       {  320, 240 },  /* QVGA */
+       {  352, 288 },  /* CIF */
+       {  640, 480 },  /* VGA */
+       { 1280, 960 },  /* SXGA */
+};
+
+#endif /* ifndef OV9640_H */
+
diff --git a/drivers/media/video/omap/sensor_if.h b/drivers/media/video/omap/sensor_if.h
new file mode 100644 (file)
index 0000000..984ba55
--- /dev/null
@@ -0,0 +1,49 @@
+
+/*
+ * drivers/media/video/omap/sensor_if.h
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc. 
+ * 
+ * Sensor interface to OMAP camera capture drivers
+ * Sensor driver should implement this interface
+ *
+ * This package is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. 
+ * 
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
+ */
+#ifndef OMAP_SENSOR_IF_H
+#define OMAP_SENSOR_IF_H
+#define LEN_SENSOR_NAME                31
+
+struct camera_sensor {
+       unsigned int version;
+       char name[LEN_SENSOR_NAME + 1];
+
+       void *(*init)(struct v4l2_pix_format *);
+       int (*cleanup)(void *);
+
+       int (*power_on)(void *);
+       int (*power_off)(void *);
+
+       int (*enum_pixformat)(struct v4l2_fmtdesc *, void *);
+       int (*try_format) (struct v4l2_pix_format *, void *);
+
+       unsigned long (*calc_xclk) (struct v4l2_pix_format *,
+               struct v4l2_fract *, void *);
+
+       int (*configure) (struct v4l2_pix_format *, unsigned long,
+                                         struct v4l2_fract *, void *);
+
+       int (*query_control) (struct v4l2_queryctrl *, void *);
+       int (*get_control) (struct  v4l2_control *, void *);
+       int (*set_control) (struct  v4l2_control *, void *);
+
+};
+       
+#endif
diff --git a/drivers/media/video/omap/sensor_ov9640.c b/drivers/media/video/omap/sensor_ov9640.c
new file mode 100644 (file)
index 0000000..c48f401
--- /dev/null
@@ -0,0 +1,1181 @@
+
+/*
+ * drivers/media/video/omap/sensor_ov9640.c
+ *
+ * Ov9640 Sensor driver for OMAP camera sensor interface
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License 
+ * version 2. This program is licensed "as is" without any warranty of any 
+ * kind, whether express or implied.
+ */
+
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <media/video-buf.h>
+#include <linux/delay.h>
+#include <asm/mach-types.h>
+#include <asm/arch/gpioexpander.h>
+
+#include "sensor_if.h"
+#include "ov9640.h"
+
+#define CAMERA_OV9640
+#ifdef CAMERA_OV9640
+
+struct ov9640_sensor {
+       /* I2C parameters */
+       struct i2c_client client;
+       struct i2c_driver driver;
+       int ver; /* OV9640 version */
+};
+
+static struct ov9640_sensor ov9640;
+
+/* list of image formats supported by OV9640 sensor */
+const static struct v4l2_fmtdesc ov9640_formats[] = {
+       {
+               /* Note:  V4L2 defines RGB565 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 r4 r3 r2 r1 r0   b4 b3 b2 b1 b0 g5 g4 g3
+                *
+                * We interpret RGB565 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 b4 b3 b2 b1 b0   r4 r3 r2 r1 r0 g5 g4 g3
+                */
+               .description    = "RGB565, le",
+               .pixelformat    = V4L2_PIX_FMT_RGB565,
+       },{
+               /* Note:  V4L2 defines RGB565X as:
+                *
+                *      Byte 0                    Byte 1
+                *      b4 b3 b2 b1 b0 g5 g4 g3   g2 g1 g0 r4 r3 r2 r1 r0
+                *
+                * We interpret RGB565X as:
+                *
+                *      Byte 0                    Byte 1
+                *      r4 r3 r2 r1 r0 g5 g4 g3   g2 g1 g0 b4 b3 b2 b1 b0
+                */
+               .description    = "RGB565, be",
+               .pixelformat    = V4L2_PIX_FMT_RGB565X,
+       },
+       {
+               .description    = "YUYV (YUV 4:2:2), packed",
+               .pixelformat    = V4L2_PIX_FMT_YUYV,
+       },{
+               .description    = "UYVY, packed",
+               .pixelformat    = V4L2_PIX_FMT_UYVY,
+       },
+       {
+               /* Note:  V4L2 defines RGB555 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 r4 r3 r2 r1 r0   x  b4 b3 b2 b1 b0 g4 g3
+                *
+                * We interpret RGB555 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 b4 b3 b2 b1 b0   x  r4 r3 r2 r1 r0 g4 g3
+                */
+               .description    = "RGB555, le",
+               .pixelformat    = V4L2_PIX_FMT_RGB555,
+       },{
+               /* Note:  V4L2 defines RGB555X as:
+                *
+                *      Byte 0                    Byte 1
+                *      x  b4 b3 b2 b1 b0 g4 g3   g2 g1 g0 r4 r3 r2 r1 r0
+                *
+                * We interpret RGB555X as:
+                *
+                *      Byte 0                    Byte 1
+                *      x  r4 r3 r2 r1 r0 g4 g3   g2 g1 g0 b4 b3 b2 b1 b0
+                */
+               .description    = "RGB555, be",
+               .pixelformat    = V4L2_PIX_FMT_RGB555X,
+       }
+};
+
+#define NUM_CAPTURE_FORMATS (sizeof(ov9640_formats)/sizeof(ov9640_formats[0]))
+#define NUM_OVERLAY_FORMATS 2
+
+/* register initialization tables for OV9640 */
+
+#define OV9640_REG_TERM 0xFF   /* terminating list entry for reg */
+#define OV9640_VAL_TERM 0xFF   /* terminating list entry for val */
+
+/* common OV9640 register initialization for all image sizes, pixel formats, 
+ * and frame rates
+ */
+const static struct ov9640_reg ov9640_common[] = {
+       { 0x12, 0x80 }, { 0x11, 0x80 }, { 0x13, 0x88 }, /* COM7, CLKRC, COM8 */
+       { 0x01, 0x58 }, { 0x02, 0x24 }, { 0x04, 0x00 }, /* BLUE, RED, COM1 */
+       { 0x0E, 0x81 }, { 0x0F, 0x4F }, { 0x14, 0xcA }, /* COM5, COM6, COM9 */
+       { 0x16, 0x02 }, { 0x1B, 0x01 }, { 0x24, 0x70 }, /* ?, PSHFT, AEW */
+       { 0x25, 0x68 }, { 0x26, 0xD3 }, { 0x27, 0x90 }, /* AEB, VPT, BBIAS */
+       { 0x2A, 0x00 }, { 0x2B, 0x00 }, { 0x32, 0x24 }, /* EXHCH, EXHCL, HREF */
+       { 0x33, 0x02 }, { 0x37, 0x02 }, { 0x38, 0x13 }, /* CHLF, ADC, ACOM */
+       { 0x39, 0xF0 }, { 0x3A, 0x00 }, { 0x3B, 0x01 }, /* OFON, TSLB, COM11 */
+       { 0x3D, 0x90 }, { 0x3E, 0x02 }, { 0x3F, 0xF2 }, /* COM13, COM14, EDGE */
+       { 0x41, 0x02 }, { 0x42, 0xC8 },         /* COM16, COM17 */
+       { 0x43, 0xF0 }, { 0x44, 0x10 }, { 0x45, 0x6C }, /* ?, ?, ? */
+       { 0x46, 0x6C }, { 0x47, 0x44 }, { 0x48, 0x44 }, /* ?, ?, ? */
+       { 0x49, 0x03 }, { 0x59, 0x49 }, { 0x5A, 0x94 }, /* ?, ?, ? */
+       { 0x5B, 0x46 }, { 0x5C, 0x84 }, { 0x5D, 0x5C }, /* ?, ?, ? */
+       { 0x5E, 0x08 }, { 0x5F, 0x00 }, { 0x60, 0x14 }, /* ?, ?, ? */
+       { 0x61, 0xCE },                                 /* ? */
+       { 0x62, 0x70 }, { 0x63, 0x00 }, { 0x64, 0x04 }, /* LCC1, LCC2, LCC3 */
+       { 0x65, 0x00 }, { 0x66, 0x00 },                 /* LCC4, LCC5 */
+       { 0x69, 0x00 }, { 0x6A, 0x3E }, { 0x6B, 0x3F }, /* HV, MBD, DBLV */
+       { 0x6C, 0x40 }, { 0x6D, 0x30 }, { 0x6E, 0x4B }, /* GSP1, GSP2, GSP3 */
+       { 0x6F, 0x60 }, { 0x70, 0x70 }, { 0x71, 0x70 }, /* GSP4, GSP5, GSP6 */
+       { 0x72, 0x70 }, { 0x73, 0x70 }, { 0x74, 0x60 }, /* GSP7, GSP8, GSP9 */
+       { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, /* GSP10,GSP11,GSP12 */
+       { 0x78, 0x3A }, { 0x79, 0x2E }, { 0x7A, 0x28 }, /* GSP13,GSP14,GSP15 */
+       { 0x7B, 0x22 }, { 0x7C, 0x04 }, { 0x7D, 0x07 }, /* GSP16,GST1, GST2 */
+       { 0x7E, 0x10 }, { 0x7F, 0x28 }, { 0x80, 0x36 }, /* GST3, GST4, GST5 */
+       { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, /* GST6, GST7, GST8 */
+       { 0x84, 0x6C }, { 0x85, 0x78 }, { 0x86, 0x8C }, /* GST9, GST10,GST11 */
+       { 0x87, 0x9E }, { 0x88, 0xBB }, { 0x89, 0xD2 }, /* GST12,GST13,GST14 */
+       { 0x8A, 0xE6 }, { 0x13, 0xaF }, { 0x15, 0x02 }, /* GST15, COM8 */
+       { 0x22, 0x8a }, /* GROS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+/* OV9640 register configuration for all combinations of pixel format and 
+ * image size
+ */
+       /* YUV (YCbCr) QQCIF */
+const static struct ov9640_reg qqcif_yuv[] = {
+       { 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* YUV (YCbCr) QQVGA */
+const static struct ov9640_reg qqvga_yuv[] = {
+       { 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* YUV (YCbCr) QCIF */
+const static struct ov9640_reg qcif_yuv[] = {
+       { 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* YUV (YCbCr) QVGA */
+const static struct ov9640_reg qvga_yuv[] = {
+       { 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* YUV (YCbCr) CIF */
+const static struct ov9640_reg cif_yuv[] = {
+       { 0x12, 0x20 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* YUV (YCbCr) VGA */
+const static struct ov9640_reg vga_yuv[] = {
+       { 0x12, 0x40 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* YUV (YCbCr) SXGA */
+const static struct ov9640_reg sxga_yuv[] = {
+       { 0x12, 0x00 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x0F },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 QQCIF */
+const static struct ov9640_reg qqcif_565[] = {
+       { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 QQVGA */
+const static struct ov9640_reg qqvga_565[] = {
+       { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 QCIF */
+const static struct ov9640_reg qcif_565[] = {
+       { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 QVGA */
+const static struct ov9640_reg qvga_565[] = {
+       { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 CIF */
+const static struct ov9640_reg cif_565[] = {
+       { 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 VGA */
+const static struct ov9640_reg vga_565[] = {
+       { 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB565 SXGA */
+const static struct ov9640_reg sxga_565[] = {
+       { 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 QQCIF */
+const static struct ov9640_reg qqcif_555[] = {
+       { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 QQVGA */
+const static struct ov9640_reg qqvga_555[] = {
+       { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 QCIF */
+const static struct ov9640_reg qcif_555[] = {
+       { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 QVGA */
+const static struct ov9640_reg qvga_555[] = {
+       { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 CIF */
+const static struct ov9640_reg cif_555[] = {
+       { 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 VGA */
+const static struct ov9640_reg vga_555[] = {
+       { 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+       /* RGB555 SXGA */
+const static struct ov9640_reg sxga_555[] = {
+       { 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */
+       { 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */
+       { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */
+       { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */
+       { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */
+       { 0x58, 0x65 },                                 /* MTXS */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+#define DEF_GAIN         31
+#define DEF_AUTOGAIN      1
+#define DEF_EXPOSURE    154
+#define DEF_AEC           1
+#define DEF_FREEZE_AGCAEC 0
+#define DEF_BLUE        153
+#define DEF_RED         (255 - DEF_BLUE)
+#define DEF_AWB           1
+#define DEF_HFLIP         0
+#define DEF_VFLIP         0
+
+/* Our own specific controls */
+#define V4L2_CID_FREEZE_AGCAEC V4L2_CID_PRIVATE_BASE+0
+#define V4L2_CID_AUTOEXPOSURE V4L2_CID_PRIVATE_BASE+1
+#define V4L2_CID_LAST_PRIV  V4L2_CID_AUTOEXPOSURE
+
+/*  Video controls  */
+static struct vcontrol {
+        struct v4l2_queryctrl qc;
+        int current_value;
+        u8 reg;
+        u8 mask;
+        u8 start_bit;
+} control[] = {
+        { { V4L2_CID_GAIN, V4L2_CTRL_TYPE_INTEGER, "Gain", 0, 63, 1,
+            DEF_GAIN },
+          0, OV9640_GAIN, 0x3f, 0 },
+        { { V4L2_CID_AUTOGAIN, V4L2_CTRL_TYPE_BOOLEAN, "Auto Gain", 0, 1, 0,
+            DEF_AUTOGAIN },
+          0, OV9640_COM8, 0x04, 2 },
+        { { V4L2_CID_EXPOSURE, V4L2_CTRL_TYPE_INTEGER, "Exposure", 0, 255, 1,
+            DEF_EXPOSURE },
+          0, OV9640_AECH, 0xff, 0 },
+        { { V4L2_CID_AUTOEXPOSURE, V4L2_CTRL_TYPE_BOOLEAN, "Auto Exposure", 0, 1, 0,
+            DEF_AEC },
+          0, OV9640_COM8, 0x01, 0 },
+        { { V4L2_CID_FREEZE_AGCAEC, V4L2_CTRL_TYPE_BOOLEAN, "Freeze AGC/AEC", 0,1,0,
+            DEF_FREEZE_AGCAEC },
+          0, OV9640_COM9, 0x01, 0 },
+        { { V4L2_CID_RED_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Red Balance", 0, 255, 1,
+            DEF_RED },
+          0, OV9640_RED, 0xff, 0 },
+        { { V4L2_CID_BLUE_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Blue Balance", 0, 255, 1,
+            DEF_BLUE },
+          0, OV9640_BLUE, 0xff, 0 },
+        { { V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CTRL_TYPE_BOOLEAN, "Auto White Balance", 0,1,0,
+            DEF_AWB },
+          0, OV9640_COM8, 0x02, 1 },
+        { { V4L2_CID_HFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Mirror Image", 0, 1, 0,
+            DEF_HFLIP },
+          0, OV9640_MVFP, 0x20, 5 },
+        { { V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Vertical Flip", 0, 1, 0,
+            DEF_VFLIP },
+          0, OV9640_MVFP, 0x10, 4 },
+};
+
+#define NUM_CONTROLS (sizeof(control)/sizeof(control[0]))
+
+const static struct ov9640_reg *
+       ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] =
+{
+ { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv },
+ { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 },
+ { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 },
+};
+
+
+/* 
+ * Read a value from a register in an OV9640 sensor device.  The value is 
+ * returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int 
+ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+       int err;
+       struct i2c_msg msg[1];
+       unsigned char data[1];
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       msg->addr = client->addr;
+       msg->flags = 0;
+       msg->len = 1;
+       msg->buf = data;
+       *data = reg;
+       err = i2c_transfer(client->adapter, msg, 1);
+       if (err >= 0) {
+               msg->flags = I2C_M_RD;
+               err = i2c_transfer(client->adapter, msg, 1);
+       }
+       if (err >= 0) {
+               *val = *data;
+               return 0;
+       }
+       return err;
+}
+
+/* Write a value to a register in an OV9640 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int 
+ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+       int err;
+       struct i2c_msg msg[1];
+       unsigned char data[2];
+
+       if (!client->adapter)
+               return -ENODEV;
+       
+       msg->addr = client->addr;
+       msg->flags = 0;
+       msg->len = 2;
+       msg->buf = data;
+       data[0] = reg;
+       data[1] = val;
+       err = i2c_transfer(client->adapter, msg, 1);
+       if (err >= 0)
+               return 0;
+       return err;
+}
+
+static int 
+ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+       u8 oldval, newval;
+       int rc;
+
+       if (mask == 0xff) {
+               newval = *val;
+       } else {
+               /* need to do read - modify - write */
+               if ((rc = ov9640_read_reg(client, reg, &oldval)))
+                       return rc;
+               oldval &= (~mask);              /* Clear the masked bits */
+               *val &= mask;                  /* Enforce mask on value */
+               newval = oldval | *val;        /* Set the desired bits */
+       }
+
+       /* write the new value to the register */
+       if ((rc = ov9640_write_reg(client, reg, newval)))
+               return rc;
+
+       if ((rc = ov9640_read_reg(client, reg, &newval)))
+               return rc;
+
+       *val = newval & mask;
+       return 0;
+}
+
+static int 
+ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask)
+{
+       int rc;
+
+       if ((rc = ov9640_read_reg(client, reg, val)))
+               return rc;
+       (*val) &= mask;
+
+       return 0;
+}
+
+/* Initialize a list of OV9640 registers.
+ * The list of registers is terminated by the pair of values 
+ * { OV9640_REG_TERM, OV9640_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int 
+ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[])
+{
+       int err;
+       const struct ov9640_reg *next = reglist;
+       
+       while (!((next->reg == OV9640_REG_TERM) 
+               && (next->val == OV9640_VAL_TERM)))
+       {
+               err = ov9640_write_reg(client, next->reg, next->val);
+               udelay(100);
+               if (err)
+                       return err;
+               next++;
+       }
+       return 0;
+}
+
+/* Returns the index of the requested ID from the control structure array */
+static int
+find_vctrl(int id)
+{
+       int i;
+
+       if (id < V4L2_CID_BASE)
+               return -EDOM;
+
+       for (i = NUM_CONTROLS - 1; i >= 0; i--)
+               if (control[i].qc.id == id)
+                       break;
+       if (i < 0)
+               i = -EINVAL;
+       return i;
+}
+
+/* Calculate the internal clock divisor (value of the CLKRC register) of the 
+ * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a 
+ * desired frame period (in seconds).  The frame period 'fper' is expressed as 
+ * a fraction.  The frame period is an input/output parameter.
+ * Returns the value of the OV9640 CLKRC register that will yield the frame 
+ * period returned in 'fper' at the specified xclk frequency.  The 
+ * returned period will be as close to the requested period as possible.
+ */
+static unsigned char
+ov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper)
+{
+       unsigned long fpm, fpm_max;     /* frames per minute */
+       unsigned long divisor;
+       const unsigned long divisor_max = 64;
+       const static unsigned long clks_per_frame[] = 
+               { 200000, 200000, 200000, 200000, 400000, 800000, 3200000 };
+
+       if (fper->numerator > 0)
+               fpm = (fper->denominator*60)/fper->numerator;
+       else
+               fpm = 0xffffffff;
+       fpm_max = (xclk*60)/clks_per_frame[isize];
+       if (fpm_max == 0)
+               fpm_max = 1;
+       if (fpm > fpm_max)
+               fpm = fpm_max;
+       if (fpm == 0)
+               fpm = 1;
+       divisor = fpm_max/fpm;
+       if (divisor > divisor_max)
+               divisor = divisor_max;
+       fper->numerator = divisor*60;
+       fper->denominator = fpm_max;
+
+       /* try to reduce the fraction */
+       while (!(fper->denominator % 5) && !(fper->numerator % 5)) {
+               fper->numerator /= 5;
+               fper->denominator /= 5;
+       }
+       while (!(fper->denominator % 3) && !(fper->numerator % 3)) {
+               fper->numerator /= 3;
+               fper->denominator /= 3;
+       }
+       while (!(fper->denominator % 2) && !(fper->numerator % 2)) {
+               fper->numerator /= 2;
+               fper->denominator /= 2;
+       }
+       if (fper->numerator < fper->denominator) {
+               if (!(fper->denominator % fper->numerator)) {
+                       fper->denominator /= fper->numerator;
+                       fper->numerator = 1;
+               }
+       }
+       else {
+               if (!(fper->numerator % fper->denominator)) {
+                       fper->numerator /= fper->denominator;
+                       fper->denominator = 1;
+               }
+       }
+
+       /* we set bit 7 in CLKRC to enable the digital PLL */
+       return (0x80 | (divisor - 1));
+}
+
+/* Configure the OV9640 for a specified image size, pixel format, and frame 
+ * period.  xclk is the frequency (in Hz) of the xclk input to the OV9640.  
+ * fper is the frame period (in seconds) expressed as a fraction.
+ * Returns zero if successful, or non-zero otherwise.
+ * The actual frame period is returned in fper.
+ */
+static int
+ov9640_configure(struct i2c_client *client, 
+       enum image_size isize, 
+       enum pixel_format pfmt,
+       unsigned long xclk,
+       struct v4l2_fract *fper)
+{
+       int err;
+       unsigned char clkrc;
+
+       /* common register initialization */
+       err = ov9640_write_regs(client, ov9640_common);
+       if (err)
+               return err;
+
+       /* configure image size and pixel format */
+       err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]);
+       if (err)
+               return err;
+
+       /* configure frame rate */
+       clkrc = ov9640_clkrc(isize, xclk, fper);
+       err = ov9640_write_reg(client, OV9640_CLKRC, clkrc);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int
+ov9640_powerup(void)
+{
+       unsigned char expa;
+       int err;
+
+       if (machine_is_omap_h2())
+               return 0;
+
+       /* read the current state of GPIO EXPA output */
+       if (( err = read_gpio_expa(&expa, 0x27))){
+               printk(KERN_ERR "Error reading GPIO EXPA \n");
+               return err;
+       }
+       /* set GPIO EXPA P7 CAMERA_MOD_EN to power-up sensor */
+       if ((err = write_gpio_expa(expa | 0x80, 0x27))) {
+               printk(KERN_ERR "Error writing to GPIO EXPA \n");
+               return err;
+       }
+
+       return 0;
+}
+static int
+ov9640_powerdown(void)
+{
+       unsigned char expa;
+       int err;
+
+       if (machine_is_omap_h2())
+               return 0;
+
+       /* read the current state of GPIO EXPA output */
+       if (( err = read_gpio_expa(&expa, 0x27))){
+               printk(KERN_ERR "Error reading GPIO EXPA \n");
+               return err;
+       }
+       /* clear GPIO EXPA P7 CAMERA_MOD_EN to power-up sensor */
+       if ((err = write_gpio_expa(expa & ~0x80, 0x27))) {
+               printk(KERN_ERR "Error writing to GPIO EXPA \n");
+               return err;
+       }
+       return 0;
+}
+
+static int
+ov9640sensor_power_on(void *priv)
+{
+       return ov9640_powerup();
+}
+
+static int
+ov9640sensor_power_off(void *priv)
+{
+       return ov9640_powerdown();
+}
+
+/* Detect if an OV9640 is present, and if so which revision. 
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) 
+ * and the product ID (PID) registers match the expected values.  
+ * Any value of the version ID (VER) register is accepted.
+ * Here are the version numbers we know about:
+ *     0x48 --> OV9640 Revision 1 or OV9640 Revision 2
+ *     0x49 --> OV9640 Revision 3
+ * Returns a negative error number if no device is detected, or the 
+ * non-negative value of the version ID register if a device is detected.
+ */
+static int
+ov9640_detect(struct i2c_client *client)
+{
+       u8 midh, midl, pid, ver;
+
+       if (!client)
+               return -ENODEV;
+       if (ov9640_read_reg(client, OV9640_MIDH, &midh))
+               return -ENODEV;
+       if (ov9640_read_reg(client, OV9640_MIDL, &midl))
+               return -ENODEV;
+       if (ov9640_read_reg(client, OV9640_PID, &pid))
+               return -ENODEV;
+       if (ov9640_read_reg(client, OV9640_VER, &ver))
+               return -ENODEV;
+
+       if ((midh != OV9640_MIDH_MAGIC) 
+               || (midl != OV9640_MIDL_MAGIC)
+               || (pid != OV9640_PID_MAGIC))
+       {
+               /* We didn't read the values we expected, so 
+                * this must not be an OV9640.
+                */
+               return -ENODEV;
+       }
+       return ver;
+}
+
+/* This function registers an I2C client via i2c_attach_client() for an OV9640 
+ * sensor device.  If 'probe' is non-zero, then the I2C client is only 
+ * registered if the device can be detected.  If 'probe' is zero, then no 
+ * device detection is attempted and the I2C client is always registered.
+ * Returns zero if an I2C client is successfully registered, or non-zero 
+ * otherwise.
+ */
+static int 
+ov9640_i2c_attach_client(struct i2c_adapter *adap, int addr, int probe)
+{
+       struct ov9640_sensor *sensor = &ov9640;
+       struct i2c_client *client = &sensor->client;
+       int err;
+
+       if (client->adapter)
+               return -EBUSY;  /* our client is already attached */
+
+       client->addr = addr;
+       client->flags = I2C_CLIENT_ALLOW_USE;
+       client->driver = &sensor->driver;
+       client->adapter = adap;
+
+       err = i2c_attach_client(client);
+       if (err) {
+               client->adapter = NULL;
+               return err;
+       }
+
+       if (probe) {
+               err = ov9640_detect(client);
+               if (err < 0) {
+                       i2c_detach_client(client);
+                       client->adapter = NULL;
+                       return err;
+               }
+               sensor->ver = err;
+       }
+       return 0;
+}
+
+/* This function is called by i2c_del_adapter() and i2c_del_driver() 
+ * if the adapter or driver with which this I2C client is associated is 
+ * removed.  This function unregisters the client via i2c_detach_client().
+ * Returns zero if the client is successfully detached, or non-zero 
+ * otherwise.
+ */
+static int 
+ov9640_i2c_detach_client(struct i2c_client *client)
+{
+       int err;
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       err = i2c_detach_client(client);
+       client->adapter = NULL;
+
+       return err;
+}
+
+/* This function will be called for each registered I2C bus adapter when our 
+ * I2C driver is registered via i2c_add_driver().  It will also be called 
+ * whenever a new I2C adapter is registered after our I2C driver is registered.
+ * This function probes the specified I2C bus adapter to determine if an 
+ * OV9640 sensor device is present.  If a device is detected, an I2C client 
+ * is registered for it via ov9640_i2c_attach_client().  Note that we can't use 
+ * the standard i2c_probe() function to look for the sensor because the OMAP 
+ * I2C controller doesn't support probing.
+ * Returns zero if an OV9640 device is detected and an I2C client successfully 
+ * registered for it, or non-zero otherwise.
+ */
+static int 
+ov9640_i2c_probe_adapter(struct i2c_adapter *adap)
+{
+       return ov9640_i2c_attach_client(adap, OV9640_I2C_ADDR, 1);
+}
+
+/* Find the best match for a requested image capture size.  The best match 
+ * is chosen as the nearest match that has the same number or fewer pixels 
+ * as the requested size, or the smallest image size if the requested size 
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size
+ov9640_find_size(unsigned int width, unsigned int height)
+{
+       enum image_size isize;
+       unsigned long pixels = width*height;
+
+       for (isize = QQCIF; isize < SXGA; isize++) {
+               if (ov9640_sizes[isize + 1].height*
+                       ov9640_sizes[isize + 1].width > pixels)
+               {
+                       return isize;
+               }
+       }
+       return SXGA;
+}
+
+/* following are sensor interface functions implemented by 
+ * OV9640 sensor driver.
+ */
+static int
+ov9640sensor_query_control(struct v4l2_queryctrl *qc, void *priv)
+{
+       int i;
+
+       i = find_vctrl (qc->id);
+       if (i == -EINVAL) {
+               qc->flags = V4L2_CTRL_FLAG_DISABLED;
+               return 0;
+       }
+       if (i < 0)
+               return -EINVAL;
+
+       *qc = control[i].qc;
+       return 0;
+}
+
+static int
+ov9640sensor_get_control(struct v4l2_control *vc, void *priv)
+{
+       struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv;
+       struct i2c_client *client = &sensor->client;
+       int i, val;
+       struct vcontrol * lvc;
+       
+       i = find_vctrl(vc->id);
+       if (i < 0)
+               return -EINVAL;
+
+       lvc = &control[i];      
+       if (ov9640_read_reg_mask(client, lvc->reg, (u8 *)&val, lvc->mask))
+               return -EIO;
+               
+       val = val >> lvc->start_bit;    
+       if (val >= 0) {
+               vc->value = lvc->current_value = val;
+               return 0;
+       } else
+               return val;
+}
+
+static int
+ov9640sensor_set_control(struct v4l2_control *vc, void *priv)
+{
+       struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv;
+       struct i2c_client *client = &sensor->client;
+       struct vcontrol *lvc;
+       int val = vc->value;
+       int i;
+
+       i = find_vctrl(vc->id);
+       if (i < 0)
+               return -EINVAL;
+
+       lvc = &control[i];
+       val = val << lvc->start_bit;
+       if (ov9640_write_reg_mask(client, lvc->reg, (u8 *)&val, (u8)lvc->mask))
+               return -EIO;
+
+       val = val>> lvc->start_bit;
+       if (val >= 0) {
+               lvc->current_value = val;
+               return 0;
+       } else
+               return val;
+}
+
+/* Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+static int
+ov9640sensor_enum_pixformat(struct v4l2_fmtdesc *fmt, void *priv)
+{
+       int index = fmt->index;
+       enum v4l2_buf_type type = fmt->type;
+
+       memset(fmt, 0, sizeof(*fmt));
+       fmt->index = index;
+       fmt->type = type;
+
+       switch (fmt->type) {
+               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (index >= NUM_CAPTURE_FORMATS)
+                       return -EINVAL;
+               break;
+
+               case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (index >= NUM_OVERLAY_FORMATS)
+                       return -EINVAL;
+               break;
+
+               default:
+                       return -EINVAL;
+       }
+
+       fmt->flags = ov9640_formats[index].flags;
+       strlcpy(fmt->description, ov9640_formats[index].description, sizeof(fmt->description));
+       fmt->pixelformat = ov9640_formats[index].pixelformat;
+
+       return 0;
+}
+
+/* Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.  This 
+ * ioctl is used to negotiate the image capture size and pixel format 
+ * without actually making it take effect.
+ */
+static int
+ov9640sensor_try_format(struct v4l2_pix_format *pix, void *priv)
+{
+       enum image_size isize;
+       int ifmt;
+
+       isize = ov9640_find_size(pix->width, pix->height);
+       pix->width = ov9640_sizes[isize].width;
+       pix->height = ov9640_sizes[isize].height;
+       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+               if (pix->pixelformat == ov9640_formats[ifmt].pixelformat)
+                       break;
+       }
+       if (ifmt == NUM_CAPTURE_FORMATS)
+               ifmt = 0;
+       pix->pixelformat = ov9640_formats[ifmt].pixelformat;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = pix->width*2;
+       pix->sizeimage = pix->bytesperline*pix->height;
+       pix->priv = 0;
+       switch (pix->pixelformat) {
+               case V4L2_PIX_FMT_YUYV:
+               case V4L2_PIX_FMT_UYVY:
+               default:
+                       pix->colorspace = V4L2_COLORSPACE_JPEG;
+                       break;
+               case V4L2_PIX_FMT_RGB565:
+               case V4L2_PIX_FMT_RGB565X:
+               case V4L2_PIX_FMT_RGB555:
+               case V4L2_PIX_FMT_RGB555X:
+                       pix->colorspace = V4L2_COLORSPACE_SRGB;
+                       break;
+       }
+       return 0;
+}
+
+/* Given the image capture format in pix, the nominal frame period in 
+ * timeperframe, calculate the required xclk frequency 
+ * The nominal xclk input frequency of the OV9640 is 24MHz, maximum 
+ * frequency is 48MHz, and minimum frequency is 10MHz.
+ */
+static unsigned long
+ov9640sensor_calc_xclk(struct v4l2_pix_format *pix,
+                       struct v4l2_fract *timeperframe, void *priv)
+{
+       unsigned long tgt_xclk;                 /* target xclk */
+       unsigned long tgt_fpm;                  /* target frames per minute */
+       enum image_size isize;
+
+       /* We use arbitrary rules to select the xclk frequency.  If the 
+        * capture size is VGA and the frame rate is greater than 900 
+        * frames per minute, or if the capture size is SXGA and the 
+        * frame rate is greater than 450 frames per minutes, then the 
+        * xclk frequency will be set to 48MHz.  Otherwise, the xclk 
+        * frequency will be set to 24MHz.  If the mclk frequency is such that 
+        * the target xclk frequency is not achievable, then xclk will be set 
+        * as close as to the target as possible.
+        */
+       if ((timeperframe->numerator == 0) 
+               || (timeperframe->denominator == 0))
+       {
+               /* supply a default nominal_timeperframe of 15 fps */
+               timeperframe->numerator = 1;
+               timeperframe->denominator = 15;
+       }
+       tgt_fpm = (timeperframe->denominator*60)
+               / timeperframe->numerator;
+       tgt_xclk = 24000000;
+       isize = ov9640_find_size(pix->width, pix->height);
+       switch (isize) {
+               case SXGA:
+                       if (tgt_fpm > 450)
+                               tgt_xclk = 48000000;
+                       break;
+               case VGA:
+                       if (tgt_fpm > 900)
+                               tgt_xclk = 48000000;
+                       break;
+               default:
+                       break;
+       }
+       return tgt_xclk;
+}
+
+/* Given a capture format in pix, the frame period in timeperframe, and
+ * the xclk frequency, set the capture format of the OV9640 sensor.
+ * The actual frame period will be returned in timeperframe.
+ */
+static int
+ov9640sensor_configure(struct v4l2_pix_format *pix, unsigned long xclk,
+                       struct v4l2_fract *timeperframe, void *priv)
+{
+       struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv;
+       enum pixel_format pfmt = YUV;
+
+       switch (pix->pixelformat) {
+               case V4L2_PIX_FMT_RGB565:
+               case V4L2_PIX_FMT_RGB565X:
+                       pfmt = RGB565;
+                       break;
+               case V4L2_PIX_FMT_RGB555:
+               case V4L2_PIX_FMT_RGB555X:
+                       pfmt = RGB555;
+                       break;
+               case V4L2_PIX_FMT_YUYV:
+               case V4L2_PIX_FMT_UYVY:
+               default:
+                       pfmt = YUV;
+       }
+
+       return ov9640_configure(&sensor->client,
+                               ov9640_find_size(pix->width, pix->height),
+                               pfmt, xclk, timeperframe);
+}
+
+/* Prepare for the driver to exit.
+ * Balances ov9640sensor_init().
+ * This function must de-initialize the sensor and its associated data 
+ * structures.
+ */
+static int
+ov9640sensor_cleanup(void *priv)
+{
+       struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv;
+
+       if (sensor) {
+               i2c_del_driver(&sensor->driver);
+               ov9640_powerdown();
+       }
+       return 0;
+}
+
+/* Initialize the OV9640 sensor.
+ * This routine allocates and initializes the data structure for the sensor, 
+ * powers up the sensor, registers the I2C driver, and sets a default image 
+ * capture format in pix.  The capture format is not actually programmed 
+ * into the OV9640 sensor by this routine.
+ * This function must return a non-NULL value to indicate that 
+ * initialization is successful.
+ */
+static void *
+ov9640sensor_init(struct v4l2_pix_format *pix)
+{
+       struct ov9640_sensor *sensor = &ov9640;
+       struct i2c_driver *driver = &sensor->driver;
+       int err;
+
+       memset(sensor, 0, sizeof(*sensor));
+       /* power-up the sensor */
+       if (ov9640_powerup())
+               return NULL;
+
+       driver->owner = THIS_MODULE;
+       strlcpy(driver->name, "OV9640 I2C driver", sizeof(driver->name));
+       driver->id = I2C_DRIVERID_EXP0;
+       driver->flags = I2C_DF_NOTIFY;
+       driver->attach_adapter = ov9640_i2c_probe_adapter;
+       driver->detach_client = ov9640_i2c_detach_client;
+
+       err = i2c_add_driver(driver);
+       if (err) {
+               printk(KERN_ERR "Failed to register OV9640 I2C client.\n");
+               return NULL;
+       }
+       if (!sensor->client.adapter) {
+               printk(KERN_WARNING 
+                       "Failed to detect OV9640 sensor chip.\n");
+               return NULL;
+       }
+       else {
+               printk(KERN_INFO 
+                       "OV9640 sensor chip version 0x%02x detected\n", sensor->ver);
+       }
+
+       /* Make the default capture format QCIF RGB565 */
+       pix->width = ov9640_sizes[QCIF].width;
+       pix->height = ov9640_sizes[QCIF].height;
+       pix->pixelformat = V4L2_PIX_FMT_RGB565;
+       ov9640sensor_try_format(pix, NULL);
+
+       return (void *)sensor;
+}
+
+struct camera_sensor camera_sensor_if = {
+       version:        0x01,
+       name:           "OV9640",
+       init:           ov9640sensor_init,
+       cleanup:        ov9640sensor_cleanup, 
+       enum_pixformat: ov9640sensor_enum_pixformat,
+       try_format:     ov9640sensor_try_format,
+       calc_xclk:      ov9640sensor_calc_xclk,
+       configure:      ov9640sensor_configure,
+       query_control:  ov9640sensor_query_control, 
+       get_control:    ov9640sensor_get_control, 
+       set_control:    ov9640sensor_set_control,
+       power_on:       ov9640sensor_power_on,
+       power_off:      ov9640sensor_power_off,
+};
+
+void print_ov9640_regs(void *priv)
+{
+       struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv;
+       u8 reg, val;
+       for (reg=0x00; reg <=0x8A; reg++)
+               if (ov9640_read_reg(&sensor->client,reg,&val))
+                       printk("error reading %x\n", reg);
+               else
+                       printk("reg %x = %x\n", reg, val);       
+}
+
+#endif /* ifdef CAMERA_OV9640 */
diff --git a/include/asm-arm/arch-omap/camera.h b/include/asm-arm/arch-omap/camera.h
new file mode 100644 (file)
index 0000000..8a68945
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * OMAP-1510 camera interface
+ *
+ * FIXME: This will go to same directory with the camera driver
+ */
+#define CAMERA_BASE            0xfffb6800
+#define CAM_CTRLCLOCK_REG      (CAMERA_BASE + 0x00)
+#define CAM_IT_STATUS_REG      (CAMERA_BASE + 0x04)
+#define CAM_MODE_REG           (CAMERA_BASE + 0x08)
+#define CAM_STATUS_REG         (CAMERA_BASE + 0x0C)
+#define CAM_CAMDATA_REG                (CAMERA_BASE + 0x10)
+#define CAM_GPIO_REG           (CAMERA_BASE + 0x14)
+#define CAM_PEAK_CTR_REG       (CAMERA_BASE + 0x18)
+
+#ifndef __ASSEMBLY__
+typedef struct {
+       __u32 ctrlclock;        /* 00 */
+       __u32 it_status;        /* 04 */
+       __u32 mode;             /* 08 */
+       __u32 status;           /* 0C */
+       __u32 camdata;          /* 10 */
+       __u32 gpio;             /* 14 */
+       __u32 peak_counter;     /* 18 */
+} camera_regs_t;
+#endif
+
+/* CTRLCLOCK bit shifts */
+#define FOSCMOD_BIT            0
+#define FOSCMOD_MASK           (0x7 << FOSCMOD_BIT)
+#define          FOSCMOD_12MHz         0x0
+#define          FOSCMOD_6MHz          0x2
+#define          FOSCMOD_9_6MHz        0x4
+#define          FOSCMOD_24MHz         0x5
+#define          FOSCMOD_8MHz          0x6
+#define POLCLK                 (1<<3)
+#define CAMEXCLK_EN            (1<<4)
+#define MCLK_EN                        (1<<5)
+#define DPLL_EN                        (1<<6)
+#define LCLK_EN                        (1<<7)
+
+/* IT_STATUS bit shifts */
+#define V_UP                   (1<<0)
+#define V_DOWN                 (1<<1)
+#define H_UP                   (1<<2)
+#define H_DOWN                 (1<<3)
+#define FIFO_FULL              (1<<4)
+#define DATA_XFER              (1<<5)
+
+/* MODE bit shifts */
+#define CAMOSC                 (1<<0)
+#define IMGSIZE_BIT            1
+#define IMGSIZE_MASK           (0x3 << IMGSIZE_BIT)
+#define          IMGSIZE_CIF           (0x0 << IMGSIZE_BIT)       /* 352x288 */
+#define          IMGSIZE_QCIF          (0x1 << IMGSIZE_BIT)       /* 176x144 */
+#define          IMGSIZE_VGA           (0x2 << IMGSIZE_BIT)       /* 640x480 */
+#define          IMGSIZE_QVGA          (0x3 << IMGSIZE_BIT)       /* 320x240 */
+#define ORDERCAMD              (1<<3)
+#define EN_V_UP                        (1<<4)
+#define EN_V_DOWN              (1<<5)
+#define EN_H_UP                        (1<<6)
+#define EN_H_DOWN              (1<<7)
+#define EN_DMA                 (1<<8)
+#define THRESHOLD              (1<<9)
+#define THRESHOLD_BIT          9
+#define THRESHOLD_MASK         (0x7f<<9)
+#define EN_NIRQ                        (1<<16)
+#define EN_FIFO_FULL           (1<<17)
+#define RAZ_FIFO               (1<<18)
+
+/* STATUS bit shifts */
+#define VSTATUS                        (1<<0)
+#define HSTATUS                        (1<<1)
+
+/* GPIO bit shifts */
+#define CAM_RST                        (1<<0)