Commit 8403b938 authored by Tony Lindgren's avatar Tony Lindgren

Add OMAP camera driver

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