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