Adds OMAP camera driver.
Signed-off-by: Tony Lindgren <tony@atomide.com>
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
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
--- /dev/null
+# 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)/..
--- /dev/null
+/*
+ * 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);
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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,
+};
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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)