From 6f2a5a73dc3191a8ec62fd2ba4a963ae8717489f Mon Sep 17 00:00:00 2001 From: =?utf-8?q?Lars=20H=C3=A4ring?= Date: Fri, 28 Mar 2008 17:46:21 +0100 Subject: [PATCH 2/5] =?utf-8?q?Atmel=20image=20sensoring=20interface=20V4L2=20driver. =20Signed-off-by:=20Lars=20H=C3=A4ring=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit --- drivers/media/video/Kconfig | 7 + drivers/media/video/Makefile | 1 + drivers/media/video/atmel-isi.c | 1886 +++++++++++++++++++++++++++++++++++++++ drivers/media/video/atmel-isi.h | 252 ++++++ 4 files changed, 2146 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/atmel-isi.c create mode 100644 drivers/media/video/atmel-isi.h diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e204e7b..63a8a84 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -13,6 +13,13 @@ menuconfig VIDEO_CAPTURE_DRIVERS if VIDEO_CAPTURE_DRIVERS && VIDEO_DEV +config VIDEO_AVR32_ISI + tristate "AVR32 video support" + depends on VIDEO_DEV + ---help--- + This module makes the AVR32 Image Sensor Interface available + as a v4l2 device. + config VIDEO_ADV_DEBUG bool "Enable advanced debug functionality" default n diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 10b4d44..b605cee 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o obj-$(CONFIG_VIDEO_DPC) += dpc7146.o obj-$(CONFIG_TUNER_3036) += tuner-3036.o +obj-$(CONFIG_VIDEO_AVR32_ISI) += atmel-isi.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_BUF) += video-buf.o diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c new file mode 100644 index 0000000..45ea693 --- /dev/null +++ b/drivers/media/video/atmel-isi.c @@ -0,0 +1,1886 @@ +/* + * Copyright (c) 2007 Atmel Corporation + * + * Based on the bttv driver for Bt848 with respective copyright holders + * + * This program 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. + */ +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "atmel-isi.h" + +#define ATMEL_ISI_VERSION KERNEL_VERSION(0, 1, 0) +#define ISI_CODEC 0 + +/* Default ISI capture buffer size */ +#define ISI_CAPTURE_BUFFER_SIZE 800*600*2 +/* Default ISI video frame size */ +#define ISI_VIDEO_BUFFER_SIZE 320*240*2 +/* Default number of ISI video buffers */ +#define ISI_VIDEO_BUFFERS 4 +/* Maximum number of video buffers */ +#define ISI_VIDEO_BUFFERS_MAX 8 + +/* Interrupt mask for a single capture */ +#define ISI_CAPTURE_MASK (ISI_BIT(SOF) | ISI_BIT(FO_C_EMP)) + +/* ISI capture buffer size */ +static int capture_buffer_size = ISI_CAPTURE_BUFFER_SIZE; +/* Number of buffers used for streaming video */ +static int video_buffers = 4; +static int video_buffer_size = ISI_VIDEO_BUFFER_SIZE; + +/* Preview path horizontal size */ +static int prev_hsize = 320; +/* Preview path vertical size */ +static int prev_vsize = 240; +/* Scaling factor of the preview path */ +static int prev_decimation_factor = 1; + +/* Input image horizontal size */ +static int image_hsize = 320; + +/* Input image vertical size */ +static int image_vsize = 240; + +/* Frame rate scaler + * 1 = capture every second frame + * 2 = capture every third frame + * ... + * */ +static int frame_rate_scaler = 2; + +/* Set this value if we want to pretend a specific V4L2 output format + * This format is for the capturing interface + */ +static int capture_v4l2_fmt = V4L2_PIX_FMT_YUYV; +/* Set this value if we want to pretend a specific V4L2 output format + * This format is for the streaming interface + */ +static int streaming_v4l2_fmt = V4L2_PIX_FMT_YUYV; + +MODULE_PARM_DESC(video_buffers,"Number of frame buffers used for streaming"); +module_param(video_buffers, int, 0664); +MODULE_PARM_DESC(capture_buffer_size,"Capture buffer size"); +module_param(capture_buffer_size, int, 0664); +MODULE_PARM_DESC(image_hsize,"Horizontal size of input image"); +module_param(image_hsize, int, 0664); +MODULE_PARM_DESC(image_vsize,"Vertical size of input image"); +module_param(image_vsize, int, 0664); +MODULE_PARM_DESC(frame_rate_scaler, "Frame rate scaler"); +module_param(frame_rate_scaler, int, 0664); +MODULE_PARM_DESC(prev_hsize, "Horizontal image size of preview path output"); +module_param(prev_hsize, int, 0664); +MODULE_PARM_DESC(prev_vsize, "Vertical image size of preview path output"); +module_param(prev_vsize, int, 0664); +MODULE_PARM_DESC(prev_decimation_factor, "Preview path decimaion factor"); +module_param(prev_decimation_factor, int, 0664); +/* Single frame capturing states */ +enum { + STATE_IDLE = 0, + STATE_CAPTURE_READY, + STATE_CAPTURE_WAIT_SOF, + STATE_CAPTURE_IN_PROGRESS, + STATE_CAPTURE_DONE, + STATE_CAPTURE_ERROR, +}; + +/* Frame buffer states + * FRAME_UNUSED Frame(buffer) is not used by the ISI module -> an application + * can usually read out data in this state + * FRAME_QUEUED An application has queued the buffer in the incoming queue + * FRAME_DONE The ISI module has filled the buffer with data and placed is on + * the outgoing queue + * FRAME_ERROR Not used at the moment + * */ +enum frame_status { + FRAME_UNUSED, + FRAME_QUEUED, + FRAME_DONE, + FRAME_ERROR, +}; +/* Frame buffer descriptor + * Used by the ISI module as a linked list for the DMA controller. + */ +struct fbd { + /* Physical address of the frame buffer */ + dma_addr_t fb_address; + /* Physical address of the next fbd */ + dma_addr_t next_fbd_address; +}; + +/* Frame buffer data + */ +struct frame_buffer { + /* Frame buffer descriptor + * Used by the ISI DMA controller to provide linked list DMA operation + */ + struct fbd fb_desc; + /* Pointer to the start of the frame buffer */ + void *frame_buffer; + /* Timestamp of the captured frame */ + struct timeval timestamp; + /* Frame number of the frame */ + unsigned long sequence; + /* Buffer number*/ + int index; + /* Bytes used in the buffer for data, needed as buffers are always + * aligned to pages and thus may be bigger than the amount of data*/ + int bytes_used; + /* Mmap count + * Counter to measure how often this buffer is mmapped + */ + int mmap_count; + /* Buffer status */ + enum frame_status status; +}; + +struct atmel_isi { + /* ISI module spin lock. Protects against concurrent access of variables + * that are shared with the ISR */ + spinlock_t lock; + void __iomem *regs; + /* Pointer to the start of the fbd list */ + dma_addr_t fbd_list_start; + /* Frame buffers */ + struct frame_buffer video_buffer[ISI_VIDEO_BUFFERS_MAX]; + /* Frame buffer currently used by the ISI module */ + struct frame_buffer *current_buffer; + /* Size of a frame buffer */ + size_t capture_buffer_size; + /* Streaming status + * If set ISI is in streaming mode */ + int streaming; + /* Queue for incoming buffers + * The buffer number (index) is stored in the fifo as reference + */ + struct kfifo *grabq; + /* Spinlock for the incoming queue */ + spinlock_t grabq_lock; + /* Queue for outgoing buffers + * Buffer number is stored in the fifo as reference + */ + struct kfifo *doneq; + /* Spinlock for the incoming queue */ + spinlock_t doneq_lock; + + /* State of the ISI module in capturing mode */ + int state; + /* Pointer to ISI buffer */ + void *capture_buf; + /* Physical address of the capture buffer */ + dma_addr_t capture_phys; + /* Size of the ISI buffer */ + size_t capture_buf_size; + /* Capture/streaming wait queue */ + wait_queue_head_t capture_wq; + + struct atmel_isi_camera *camera; + struct atmel_isi_format format; + struct atmel_isi_format streaming_format; + + struct mutex mutex; + /* User counter for the streaming interface */ + int stream_users; + /* User counter of the capture interface */ + int capture_users; + /* Video device for capturing (Codec path) */ + struct video_device cdev; + /* Video device for streaming (Preview path) */ + struct video_device vdev; + struct completion reset_complete; + struct clk *pclk; + struct clk *hclk; + struct platform_device *pdev; + unsigned int irq; +}; + +#define to_atmel_isi(vdev) container_of(vdev, struct atmel_isi, vdev) + +struct atmel_isi_fh { + struct atmel_isi *isi; + unsigned int read_off; +}; + +/*----------------------------------------------------------------------------- + * Interface to the actual camera. + */ +static LIST_HEAD(camera_list); +static DEFINE_MUTEX(camera_list_mutex); + +static void avr32_isi_release_camera(struct atmel_isi *isi, + struct atmel_isi_camera *cam) +{ + mutex_lock(&camera_list_mutex); + cam->isi = NULL; + isi->camera = NULL; + module_put(cam->owner); + mutex_unlock(&camera_list_mutex); +} + +int atmel_isi_register_camera(struct atmel_isi_camera *cam) +{ + pr_debug("atmel_isi: register camera %s\n", cam->name); + + mutex_lock(&camera_list_mutex); + list_add_tail(&cam->list, &camera_list); + mutex_unlock(&camera_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_isi_register_camera); + +void atmel_isi_unregister_camera(struct atmel_isi_camera *cam) +{ + pr_debug("atmel_isi: unregister camera %s\n", cam->name); + + mutex_lock(&camera_list_mutex); + if (cam->isi) + cam->isi->camera = NULL; + list_del(&cam->list); + mutex_unlock(&camera_list_mutex); +} +EXPORT_SYMBOL_GPL(atmel_isi_unregister_camera); + +static struct atmel_isi_camera * avr32_isi_grab_camera(struct atmel_isi *isi) +{ + struct atmel_isi_camera *entry, *cam = NULL; + + mutex_lock(&camera_list_mutex); + list_for_each_entry(entry, &camera_list, list) { + /* Just grab the first camera available */ + if (!entry->isi) { + if (!try_module_get(entry->owner)) + continue; + + cam = entry; + cam->isi = isi; + pr_debug("%s: got camera: %s\n", + isi->vdev.name, cam->name); + break; + } + } + mutex_unlock(&camera_list_mutex); + + return cam; +} + +static int avr32_isi_set_camera_input(struct atmel_isi *isi) +{ + struct atmel_isi_camera *cam = isi->camera; + int ret; + u32 cr1; + u32 cr2; + + isi->format.pix.width = image_hsize; + isi->format.pix.height = image_vsize; + isi->format.pix.bytesperline = 0; + + ret = cam->set_format(cam, &isi->format); + if (ret) + return ret; + + + switch (isi->format.input_format) { + case ATMEL_ISI_PIXFMT_GREY: + cr2 = ISI_BIT(GRAYSCALE); + break; + case ATMEL_ISI_PIXFMT_CbYCrY: + cr2 = ISI_BF(YCC_SWAP, 0); + break; + case ATMEL_ISI_PIXFMT_CrYCbY: + cr2 = ISI_BF(YCC_SWAP, 1); + break; + case ATMEL_ISI_PIXFMT_YCbYCr: + cr2 = ISI_BF(YCC_SWAP, 2); + break; + case ATMEL_ISI_PIXFMT_YCrYCb: + cr2 = ISI_BF(YCC_SWAP, 3); + break; + case ATMEL_ISI_PIXFMT_RGB24: + cr2 = ISI_BIT(COL_SPACE) | ISI_BF(RGB_CFG, 0); + break; + case ATMEL_ISI_PIXFMT_BGR24: + cr2 = ISI_BIT(COL_SPACE) | ISI_BF(RGB_CFG, 1); + break; + case ATMEL_ISI_PIXFMT_RGB16: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_MODE) + | ISI_BF(RGB_CFG, 0)); + break; + case ATMEL_ISI_PIXFMT_BGR16: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_MODE) + | ISI_BF(RGB_CFG, 1)); + break; + case ATMEL_ISI_PIXFMT_GRB16: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_MODE) + | ISI_BF(RGB_CFG, 2)); + break; + case ATMEL_ISI_PIXFMT_GBR16: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_MODE) + | ISI_BF(RGB_CFG, 3)); + break; + case ATMEL_ISI_PIXFMT_RGB24_REV: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_SWAP) + | ISI_BF(RGB_CFG, 0)); + break; + case ATMEL_ISI_PIXFMT_BGR24_REV: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_SWAP) + | ISI_BF(RGB_CFG, 1)); + break; + case ATMEL_ISI_PIXFMT_RGB16_REV: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_SWAP) + | ISI_BIT(RGB_MODE) | ISI_BF(RGB_CFG, 0)); + break; + case ATMEL_ISI_PIXFMT_BGR16_REV: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_SWAP) + | ISI_BIT(RGB_MODE) | ISI_BF(RGB_CFG, 1)); + break; + case ATMEL_ISI_PIXFMT_GRB16_REV: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_SWAP) + | ISI_BIT(RGB_MODE) | ISI_BF(RGB_CFG, 2)); + break; + case ATMEL_ISI_PIXFMT_GBR16_REV: + cr2 = (ISI_BIT(COL_SPACE) | ISI_BIT(RGB_SWAP) + | ISI_BIT(RGB_MODE) | ISI_BF(RGB_CFG, 3)); + break; + default: + return -EINVAL; + } + + + cr1 = ISI_BF(EMB_SYNC, cam->has_emb_sync) + | ISI_BF(HSYNC_POL, cam->hsync_act_low) + | ISI_BF(VSYNC_POL, cam->vsync_act_low) + | ISI_BF(PIXCLK_POL, cam->pclk_act_falling) + | ISI_BIT(DIS); + + isi_writel(isi, CR1, cr1); + isi_writel(isi, CR2, cr2); + + return 0; +} + +static int avr32_isi_capture_set_format(struct atmel_isi *isi, + struct atmel_isi_format *fmt) +{ + u32 cr2; + + fmt->pix.width = min(2048U, fmt->pix.width); + fmt->pix.height = min(2048U, fmt->pix.height); + fmt->pix.bytesperline = 0; + + /* Set format if we have specified one */ + if(capture_v4l2_fmt){ + fmt->pix.pixelformat = capture_v4l2_fmt; + } + else { + /* Codec path output format */ + fmt->pix.pixelformat = V4L2_PIX_FMT_YVYU; + } + + /* The ISI module outputs either YUV 4:2:2 (codec path) + * or RGB 5:5:5 (preview path) (ISI grayscale mode is not supported + * by V4L2). Therefore two pixels will be in a 32bit word */ + fmt->pix.bytesperline = ALIGN(fmt->pix.width * 2, 4); + fmt->pix.sizeimage = fmt->pix.bytesperline * fmt->pix.height; + + cr2 = isi_readl(isi, CR2); + cr2 = ISI_BFINS(IM_VSIZE, fmt->pix.height - 1, cr2); + cr2 = ISI_BFINS(IM_HSIZE, fmt->pix.width - 1, cr2); + isi_writel(isi, CR2, cr2); + + pr_debug("set capture format: width=%d height=%d\n", + fmt->pix.width, fmt->pix.height); + + return 0; +} + +static int avr32_isi_streaming_set_format(struct atmel_isi *isi, + struct atmel_isi_format *fmt) +{ + memcpy(&isi->streaming_format, &isi->format, + sizeof(struct atmel_isi_format)); +#ifndef ISI_CODEC + fmt->pix.width = min(640U, prev_hsize); + fmt->pix.height = min(480U, prev_vsize); + fmt->pix.bytesperline = 0; + + /* Set format if we have specified one */ + if(streaming_v4l2_fmt){ + fmt->pix.pixelformat = streaming_v4l2_fmt; + } + else { + /* Preview path output format + * Would be logically V4L2_PIX_FMT_BGR555X + * but this format does not exist in the specification + * So for now we pretend V4L2_PIX_FMT_RGB555X + */ + fmt->pix.pixelformat = V4L2_PIX_FMT_RGB555X; + } + + /* The ISI module outputs either YUV 4:2:2 (codec path) + * or RGB 5:5:5 (preview path) (ISI grayscale mode is not + * supported yet. Therefore two pixels will be in a 32bit word + */ + fmt->pix.bytesperline = ALIGN(fmt->pix.width * 2, 4); + fmt->pix.sizeimage = fmt->pix.bytesperline * fmt->pix.height; + + /* These values depend on the sensor output image size */ + isi_writel(isi, PDECF, prev_decimation_factor);/* 1/16 * 16 = 1*/ + isi_writel(isi,PSIZE , ISI_BF(PREV_HSIZE,prev_hsize - 1) + | ISI_BF(PREV_VSIZE, prev_vsize - 1)); + + pr_debug("set_format: cr1=0x%08x cr2=0x%08x\n", + isi_readl(isi, CR1), isi_readl(isi, CR2)); +#else + avr32_isi_capture_set_format(isi, &isi->streaming_format); +#endif + return 0; +} + +static int avr32_isi_start_capture(struct atmel_isi *isi) +{ + u32 cr1; + int ret; + + spin_lock_irq(&isi->lock); + isi->state = STATE_IDLE; + isi_readl(isi, SR); /* clear any pending SOF interrupt */ + isi_writel(isi, IER, ISI_BIT(SOF)); + isi_writel(isi, CR1, isi_readl(isi, CR1) & ~ISI_BIT(DIS)); + spin_unlock_irq(&isi->lock); + + pr_debug("isi: waiting for SOF\n"); + ret = wait_event_interruptible(isi->capture_wq, + isi->state != STATE_IDLE); + if (ret) + return ret; + if (isi->state != STATE_CAPTURE_READY) + return -EIO; + + /* + * Do a codec request. Next SOF indicates start of capture, + * the one after that indicates end of capture. + */ + pr_debug("isi: starting capture\n"); + isi_writel(isi, CDBA, isi->capture_phys); + + spin_lock_irq(&isi->lock); + isi->state = STATE_CAPTURE_WAIT_SOF; + cr1 = isi_readl(isi, CR1); + cr1 |= ISI_BIT(CODEC_ON); + isi_writel(isi, CR1, cr1); + isi_writel(isi, IER, ISI_CAPTURE_MASK); + spin_unlock_irq(&isi->lock); + + return 0; +} + +static void avr32_isi_capture_done(struct atmel_isi *isi, + int state) +{ + u32 cr1; + + cr1 = isi_readl(isi, CR1); + cr1 &= ~ISI_BIT(CODEC_ON); + isi_writel(isi, CR1, cr1); + + isi->state = state; + wake_up_interruptible(&isi->capture_wq); + isi_writel(isi, IDR, ISI_CAPTURE_MASK); +} + +static irqreturn_t avr32_isi_handle_streaming(struct atmel_isi *isi, + int sequence){ + + int reqnr; + + if(kfifo_get(isi->grabq, (unsigned char *) &reqnr, + sizeof(int)) != sizeof(int)){ + + /* as no new buffer is available we keep the + * current one + */ + pr_debug("isi: dropping frame\n"); +#ifdef ISI_CODEC + isi_writel(isi, CDBA, + isi->current_buffer->fb_desc.fb_address); + + isi_writel(isi, CR1, ISI_BIT(CODEC_ON) | + isi_readl(isi, CR1)); +#else + /* TEST this has to be tested if it messes up the ISI + * streaming process */ + isi_writel(isi, PPFBD, (unsigned long) + &isi->video_buffer[isi->current_buffer->index]); +#endif + } + else{ + isi->current_buffer->status = FRAME_DONE; + isi->current_buffer->sequence = sequence; + + do_gettimeofday(&isi->current_buffer->timestamp); + + /*isi->current_buffer->bytes_used = + ISI_VIDEO_MAX_FRAME_SIZE; */ + + kfifo_put(isi->doneq, (unsigned char *) + &(isi->current_buffer->index), sizeof(int)); + + isi->current_buffer = &(isi->video_buffer[reqnr]); +#ifdef ISI_CODEC + isi_writel(isi, CDBA, + isi->current_buffer->fb_desc.fb_address); + isi_writel(isi, CR1, ISI_BIT(CODEC_ON) | + isi_readl(isi, CR1)); +#else + /*TODO check if fbd corresponds to frame buffer */ +#endif + wake_up_interruptible(&isi->capture_wq); + } + return IRQ_HANDLED; +} + +/* FIXME move code from ISR here +static irqreturn_t avr32_isi_handle_capturing(struct atmel_isi *isi){ + +}*/ +/* isi interrupt service routine */ +static irqreturn_t isi_interrupt(int irq, void *dev_id) +{ + struct atmel_isi *isi = dev_id; + u32 status, mask, pending; + irqreturn_t ret = IRQ_NONE; + static int sequence = 0; + + spin_lock(&isi->lock); + + status = isi_readl(isi, SR); + mask = isi_readl(isi, IMR); + pending = status & mask; + + pr_debug("isi: interrupt status %x pending %x\n", + status, pending); + if(isi->streaming){ + if(likely(pending & (ISI_BIT(FO_C_EMP) | ISI_BIT(FO_P_EMP)))){ + + sequence++; + ret = avr32_isi_handle_streaming(isi, sequence); + } + } + else{ + while (pending) { + if (pending & (ISI_BIT(FO_C_OVF) | ISI_BIT(FR_OVR))) { + avr32_isi_capture_done(isi, STATE_CAPTURE_ERROR); + pr_debug("%s: FIFO overrun (status=0x%x)\n", + isi->vdev.name, status); + } else if (pending & ISI_BIT(SOF)) { + switch (isi->state) { + case STATE_IDLE: + isi->state = STATE_CAPTURE_READY; + wake_up_interruptible(&isi->capture_wq); + break; + case STATE_CAPTURE_READY: + break; + case STATE_CAPTURE_WAIT_SOF: + isi->state = STATE_CAPTURE_IN_PROGRESS; + break; + /* + case STATE_CAPTURE_IN_PROGRESS: + avr32_isi_capture_done(isi, STATE_CAPTURE_DONE); + break; + */ + } + } + if (pending & ISI_BIT(FO_C_EMP)){ + if( isi->state == STATE_CAPTURE_IN_PROGRESS) + avr32_isi_capture_done(isi, STATE_CAPTURE_DONE); + } + + if (pending & ISI_BIT(SOFTRST)) { + complete(&isi->reset_complete); + isi_writel(isi, IDR, ISI_BIT(SOFTRST)); + } + + status = isi_readl(isi, SR); + mask = isi_readl(isi, IMR); + pending = status & mask; + ret = IRQ_HANDLED; + } + } + spin_unlock(&isi->lock); + + return ret; +} + +/* ------------------------------------------------------------------------ + * IOCTL videoc handling + * ----------------------------------------------------------------------*/ + +/* --------Capture ioctls ------------------------------------------------*/ +/* Device capabilities callback function. + */ +static int avr32_isi_capture_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "atmel-isi"); + strcpy(cap->card, "Atmel Image Sensor Interface"); + cap->version = ATMEL_ISI_VERSION; + /* V4L2_CAP_VIDEO_CAPTURE -> This is a capture device + * V4L2_CAP_READWRITE -> read/write interface used + */ + cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_READWRITE + ); + return 0; +} + +/* Input enumeration callback function. + * Enumerates available input devices. + * This can be called many times from the V4L2-layer by + * incrementing the index to get all avaliable input devices. + */ +static int avr32_isi_capture_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + /* Just one input (ISI) is available */ + if (input->index != 0) + return -EINVAL; + + /* Set input name as camera name */ + strlcpy(input->name, isi->camera->name, sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + /* Set to this value just because this should be set to a + * defined value + */ + input->std = V4L2_STD_PAL; + + return 0; +} +/* Selects an input device. + * One input device (ISI) currently supported. + */ +static int avr32_isi_capture_s_input(struct file *file, void *priv, + unsigned int index) +{ + if (index != 0) + return -EINVAL; + return 0; +} + +/* Gets current input device. + */ +static int avr32_isi_capture_g_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + return 0; +} + +/* Format callback function + * Returns a v4l2_fmtdesc structure with according values to a + * index. + * This function is called from user space until it returns + * -EINVAL. + */ +static int avr32_isi_capture_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index != 0) + return -EINVAL; + + /* if we want to pretend another ISI output + * this is usefull if we input an other input format from a camera + * than specified in the ISI -> makes it possible to swap bytes + * in the ISI output format but messes up the preview path output + */ + if(capture_v4l2_fmt){ + fmt->pixelformat = capture_v4l2_fmt; + } + else { + /* This is the format the ISI tries to output */ + strcpy(fmt->description, "YCbYCr (YUYV) 4:2:2"); + fmt->pixelformat = V4L2_PIX_FMT_YUYV; + } + + return 0; +} + +static int avr32_isi_capture_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vfmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + /* Just return the current format for now */ + memcpy(&vfmt->fmt.pix, &isi->format.pix, + sizeof(struct v4l2_pix_format)); + + return 0; +} + +/* Gets current hardware configuration + * For capture devices the pixel format settings are + * important. + */ +static int avr32_isi_capture_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vfmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + /* Return current pixel format */ + memcpy(&vfmt->fmt.pix, &isi->format.pix, + sizeof(struct v4l2_pix_format)); + + return 0; +} + +static int avr32_isi_capture_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vfmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + int ret = 0; + + /* We have a fixed format so just copy the current format + * back + */ + memcpy(&vfmt->fmt.pix, &isi->format.pix, + sizeof(struct v4l2_pix_format)); + + return ret; +} + +/* ------------ Preview path ioctls ------------------------------*/ +/* Device capabilities callback function. + */ +static int avr32_isi_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "atmel-isi"); + strcpy(cap->card, "Atmel Image Sensor Interface"); + cap->version = ATMEL_ISI_VERSION; + /* V4L2_CAP_VIDEO_CAPTURE -> This is a capture device + * V4L2_CAP_READWRITE -> read/write interface used + * V4L2_CAP_STREAMING -> ioctl + mmap interface used + */ + cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_READWRITE + | V4L2_CAP_STREAMING + ); + return 0; +} + +/* Input enumeration callback function. + * Enumerates available input devices. + * This can be called many times from the V4L2-layer by + * incrementing the index to get all avaliable input devices. + */ +static int avr32_isi_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + /* Just one input (ISI) is available */ + if (input->index != 0) + return -EINVAL; + + /* Set input name as camera name */ + strlcpy(input->name, isi->camera->name, sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + /* Set to this value just because this should be set to a + * defined value + */ + input->std = V4L2_STD_PAL; + + return 0; +} + +/* Selects an input device. + * One input device (ISI) currently supported. + */ +static int avr32_isi_s_input(struct file *file, void *priv, + unsigned int index) +{ + if (index != 0) + return -EINVAL; + + return 0; +} + +/* Gets current input device. + */ +static int avr32_isi_g_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + return 0; +} + +/* Format callback function + * Returns a v4l2_fmtdesc structure with according values to a + * index. + * This function is called from user space until it returns + * -EINVAL. + */ +static int avr32_isi_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + if (fmt->index != 0) + return -EINVAL; + + /* TODO: Return all possible formats + * This depends on ISI and camera. + * A enum_fmt function or a data structure should be + * added to the camera driver. + * For now just one format supported + */ + if(streaming_v4l2_fmt){ + strcpy(fmt->description, "Pretended format"); + } + else{ + strcpy(fmt->description, "Normal format"); + } + fmt->pixelformat = isi->streaming_format.pix.pixelformat;//V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int avr32_isi_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vfmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + /* FIXME For now we just return the current format*/ + memcpy(&vfmt->fmt.pix, &isi->streaming_format.pix, + sizeof(struct v4l2_pix_format)); + return 0; +} + +/* Gets current hardware configuration + * For capture devices the pixel format settings are + * important. + */ +static int avr32_isi_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vfmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + + /*Copy current pixel format structure to user space*/ + memcpy(&vfmt->fmt.pix, &isi->streaming_format.pix, + sizeof(struct v4l2_pix_format)); + + return 0; +} + +static int avr32_isi_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vfmt) +{ + struct atmel_isi_fh *fh = priv; + struct atmel_isi *isi = fh->isi; + int ret = 0; + + /* Just return the current format as we do not support + * format switching */ + memcpy(&vfmt->fmt.pix, &isi->streaming_format.pix, + sizeof(struct v4l2_pix_format)); + + return ret; +} + +/* Checks if control is supported in driver + * No controls currently supported yet + */ +static int avr32_isi_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + switch(qc->id){ + case V4L2_CID_BRIGHTNESS: + strcpy(qc->name, "Brightness"); + qc->minimum = 0; + qc->maximum = 100; + qc->step = 1; + qc->default_value = 50; + qc->flags = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int avr32_isi_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + switch(ctrl->id){ + case V4L2_CID_BRIGHTNESS: + ctrl->value = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int avr32_isi_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + switch(ctrl->id){ + case V4L2_CID_BRIGHTNESS: + break; + default: + return -EINVAL; + } + return 0; +} + +static int avr32_isi_reqbufs(struct file *file, void *private_data, + struct v4l2_requestbuffers *req) +{ + /* Only memory mapped buffers supported*/ + if(req->memory != V4L2_MEMORY_MMAP){ + pr_debug("atmel_isi: buffer format not supported\n"); + return -EINVAL; + } + pr_debug("atmel_isi: Requested %d buffers. Using %d buffers\n", + req->count, video_buffers); + /* buffer number is fixed for now as it is difficult to get + * that memory at runtime */ + req->count = video_buffers; + memset(&req->reserved, 0, sizeof(req->reserved)); + return 0; +} + +static int avr32_isi_querybuf(struct file *file, void *private_data, + struct v4l2_buffer *buf) +{ + struct atmel_isi_fh *fh = private_data; + struct atmel_isi *isi = fh->isi; + struct frame_buffer *buffer; + + if(unlikely(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + if(unlikely(buf->index >= video_buffers)) + return -EINVAL; + + buffer = &(isi->video_buffer[buf->index]); + + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->length = video_buffer_size; + buf->memory = V4L2_MEMORY_MMAP; + + /* set index as mmap reference to the buffer */ + buf->m.offset = buf->index << PAGE_SHIFT; + + switch(buffer->status){ + case FRAME_UNUSED: + case FRAME_ERROR: + case FRAME_QUEUED: + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->bytesused = buffer->bytes_used; + break; + case FRAME_DONE: + buf->flags |= V4L2_BUF_FLAG_DONE; + buf->bytesused = buffer->bytes_used; + buf->sequence = buffer->sequence; + buf->timestamp = buffer->timestamp; + break; + } + + buf->field = V4L2_FIELD_NONE; /* no interlacing stuff */ + + if(buffer->mmap_count) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + else + buf->flags &= ~V4L2_BUF_FLAG_MAPPED; + + pr_debug("atmel_isi: querybuf index:%d offset:%d\n", + buf->index, buf->m.offset); + + return 0; +} + +static int avr32_isi_qbuf(struct file *file, void *private_data, + struct v4l2_buffer *buf) +{ + struct atmel_isi_fh *fh = private_data; + struct atmel_isi *isi = fh->isi; + struct frame_buffer *buffer; + + if(unlikely(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + if(unlikely(buf->index >= video_buffers || buf->index < 0)){ + pr_debug("Buffer index is not valid index=%d\n",buf->index); + return -EINVAL; + } + if(unlikely(buf->memory != V4L2_MEMORY_MMAP)){ + pr_debug("Buffer is not of MEMORY_MMAP type\n"); + return -EINVAL; + } + + buffer = &(isi->video_buffer[buf->index]); + if(unlikely(buffer->status != FRAME_UNUSED)){ + pr_debug("Can't queue non unused frames\n"); + return -EINVAL; + } + + mutex_lock(&isi->mutex); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + buffer->status = FRAME_QUEUED; + kfifo_put(isi->grabq, (unsigned char*) &buf->index, sizeof(int)); + mutex_unlock(&isi->mutex); + + return 0; +} + +static int avr32_isi_dqbuf(struct file *file, void *private_data, + struct v4l2_buffer *buf) +{ + struct atmel_isi_fh *fh = private_data; + struct atmel_isi *isi = fh->isi; + struct frame_buffer *buffer; + int reqnr = 0; + + if(unlikely(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + /* Mencoder does not set this flag + * + if(unlikely(buf->memory != V4L2_MEMORY_MMAP)){ + pr_debug("isi: dequeue failed buffer not of mmapped type\n"); + return -EINVAL; + }*/ + if((kfifo_len(isi->doneq) == 0) ){//&& (file->f_flags & O_NONBLOCK)){ + pr_debug("Done-queue is empty\n"); + return -EAGAIN; + } + /* + if(wait_event_interruptible(isi->capture_wq, + kfifo_len(isi->doneq) != 0) < 0){ + pr_debug("Done-queue interrupted\n"); + return -EINTR; + } + */ + if(!kfifo_get(isi->doneq, (unsigned char*) &reqnr, sizeof(int))){ + pr_debug("No new buffer ready\n"); + return -EBUSY; + } + buffer = &(isi->video_buffer[reqnr]); + + if(unlikely(buffer->status != FRAME_DONE)){ + if(isi->streaming == 0) + return 0; + pr_debug("isi: error, dequeued buffer not ready\n"); + return -EINVAL; + } + buf->index = reqnr; + buf->bytesused = buffer->bytes_used; + buf->timestamp = buffer->timestamp; + buf->sequence = buffer->sequence; + buf->m.offset = reqnr << PAGE_SHIFT; + buffer->status = FRAME_UNUSED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + + buf->length = isi->capture_buffer_size; + buf->field = V4L2_FIELD_NONE; + buf->memory = V4L2_MEMORY_MMAP; + return 0; +} + +static int avr32_isi_streamon(struct file *file, void *private_data, + enum v4l2_buf_type type) +{ + struct atmel_isi_fh *fh = private_data; + struct atmel_isi *isi = fh->isi; + int reqnr; + struct frame_buffer *buffer; + u32 cr1; + + if(unlikely(type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + + if(!kfifo_get(isi->grabq, (unsigned char*) &reqnr, sizeof(int))){ + mutex_unlock(&isi->mutex); + pr_debug("atmel_isi: No buffer in IN-Queue, start of streaming\ + aborted (one buffer is required in IN-Queue)\n"); + return -EINVAL; + } + buffer = &(isi->video_buffer[reqnr]); + + + spin_lock_irq(isi->lock); + isi->streaming = 1; + isi->current_buffer = buffer; + cr1 = isi_readl(isi, CR1); +#ifdef ISI_CODEC + isi_writel(isi, CDBA, buffer->fb_desc.fb_address); + /* Enable codec path */ + cr1 |= ISI_BIT(CODEC_ON) | ISI_BIT(DIS); +#else + isi_writel(isi, PPFBD, isi->fbd_list_start); +#endif + /* Enable interrupts */ + isi_readl(isi, SR); + /* FIXME enable codec/preview path according to setup */ + isi_writel(isi, IER, ISI_BIT(FO_C_EMP) | ISI_BIT(FO_P_EMP)); + + cr1 |= ISI_BF(FRATE, frame_rate_scaler); + + /* Enable ISI module*/ + cr1 &= ~ISI_BIT(DIS); + isi_writel(isi, CR1, cr1); + spin_unlock_irq(isi->lock); + + isi->camera->start_capture(isi->camera); + + return 0; +} + +static int avr32_isi_streamoff(struct file *file, void *private_data, + enum v4l2_buf_type type) +{ + struct atmel_isi_fh *fh = private_data; + struct atmel_isi *isi = fh->isi; + int reqnr; + + if(unlikely(type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + + spin_lock_irq(isi->lock); + isi->streaming = 0; +#ifdef ISI_CODEC + /* Disble codec path */ + isi_writel(isi, CR1, isi_readl(isi, CR1) & (~ISI_BIT(CODEC_ON))); +#endif + /* Disable interrupts */ + isi_writel(isi, IDR, ISI_BIT(FO_C_EMP) | ISI_BIT(FO_P_EMP)); + + /* Disable ISI module*/ + isi_writel(isi, CR1, isi_readl(isi, CR1) | ISI_BIT(DIS)); + spin_unlock_irq(isi->lock); + + isi->camera->stop_capture(isi->camera); + while(kfifo_len(isi->grabq)){ + kfifo_get(isi->grabq, (unsigned char *) &reqnr, sizeof(int)); + kfifo_put(isi->doneq, (unsigned char *) &reqnr, sizeof(int)); + } + for(reqnr = 0; reqnr < video_buffers; reqnr++){ + isi->video_buffer[reqnr].status = FRAME_UNUSED; + } + pr_debug("atmel_isi: Stream off\n"); + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int avr32_isi_capture_close (struct inode *inode, struct file *file) +{ + struct atmel_isi_fh *fh = file->private_data; + struct atmel_isi *isi = fh->isi; + u32 cr1; + + mutex_lock(&isi->mutex); + + isi->capture_users--; + kfree(fh); + + /* Stop camera and ISI if driver has no users */ + if(!isi->stream_users) { + isi->camera->stop_capture(isi->camera); + + spin_lock_irq(&isi->lock); + cr1 = isi_readl(isi, CR1); + cr1 |= ISI_BIT(DIS); + isi_writel(isi, CR1, cr1); + spin_unlock_irq(&isi->lock); + } + mutex_unlock(&isi->mutex); + + return 0; +} + +static int avr32_isi_capture_open (struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct atmel_isi *isi = container_of(vdev, struct atmel_isi, cdev); + struct atmel_isi_fh *fh; + int ret = -EBUSY; + unsigned long timeout; + + pr_debug("%s: opened\n", vdev->name); + + mutex_lock(&isi->mutex); + + + if (isi->capture_users) { + pr_debug("%s: open(): device busy\n", vdev->name); + goto out; + } + + if (!isi->camera) { + + ret = -ENODEV; + isi->camera = avr32_isi_grab_camera(isi); + if (!isi->camera) + goto out; + + ret = avr32_isi_set_camera_input(isi); + if(ret) + goto out; + } + + avr32_isi_capture_set_format(isi, &isi->format); + + /* + * Reset the controller and wait for completion. The + * reset will only succeed if we have a pixel clock + * from the camera. + */ + if(isi->stream_users == 0){ + + init_completion(&isi->reset_complete); + isi_writel(isi, IER, ISI_BIT(SOFTRST)); + isi_writel(isi, CR1, ISI_BIT(RST)); + + timeout = wait_for_completion_timeout(&isi->reset_complete, + msecs_to_jiffies(100)); + + isi_writel(isi, IDR, ~0UL); + if (timeout == 0) { + ret = -ETIMEDOUT; + goto out; + } + } + + ret = -ENOMEM; + fh = kzalloc(sizeof(struct atmel_isi_fh), GFP_KERNEL); + if (!fh) { + pr_debug("%s: open(): out of memory\n", vdev->name); + goto out; + } + + fh->isi = isi; + file->private_data = fh; + isi->capture_users++; + + ret = 0; + +out: + mutex_unlock(&isi->mutex); + return ret; +} + +static ssize_t avr32_isi_capture_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct atmel_isi_fh *fh = file->private_data; + struct atmel_isi *isi = fh->isi; + int state; + int ret; + + state = STATE_IDLE; + + pr_debug("isi: read %zu bytes read_off=%u state=%u sizeimage=%u\n", + count, fh->read_off, state, isi->format.pix.sizeimage); + isi->camera->start_capture(isi->camera); + + + avr32_isi_start_capture(isi); + + ret = wait_event_interruptible( isi->capture_wq, + (isi->state == STATE_CAPTURE_DONE) + || (isi->state == STATE_CAPTURE_ERROR)); + if (ret) + return ret; + if (isi->state == STATE_CAPTURE_ERROR) { + isi->state = STATE_IDLE; + return -EIO; + } + + fh->read_off = 0; + + count = min(count, (size_t)isi->format.pix.sizeimage - fh->read_off); + ret = copy_to_user(data, isi->capture_buf + fh->read_off, count); + if (ret) + return -EFAULT; + + fh->read_off += count; + if (fh->read_off >= isi->format.pix.sizeimage) + isi->state = STATE_IDLE; + + return count; +} + +static void avr32_isi_capture_release (struct video_device *vdev) +{ + pr_debug("%s: release\n", vdev->name); +} + +/* ----------------- Streaming interface -------------------------------------*/ +static void avr32_isi_vm_open(struct vm_area_struct *vma){ + struct frame_buffer *buffer = + (struct frame_buffer *) vma->vm_private_data; + buffer->mmap_count++; + pr_debug("atmel_isi: vm_open count=%d\n",buffer->mmap_count); +} + +static void avr32_isi_vm_close(struct vm_area_struct *vma){ + struct frame_buffer *buffer = + (struct frame_buffer *) vma->vm_private_data; + pr_debug("atmel_isi: vm_close count=%d\n",buffer->mmap_count); + buffer->mmap_count--; + if(buffer->mmap_count < 0) + printk("atmel_isi: mmap_count went negative\n"); +} + + +static struct vm_operations_struct avr32_isi_vm_ops = { + .open = avr32_isi_vm_open, + .close = avr32_isi_vm_close, +}; + +static int avr32_isi_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pfn; + int ret; + struct atmel_isi_fh *fh = file->private_data; + struct atmel_isi * isi = fh->isi; + struct frame_buffer *buffer = &(isi->video_buffer[vma->vm_pgoff]); + unsigned long size = vma->vm_end - vma->vm_start; + + pr_debug("atmel_isi: mmap called pgoff=%ld size=%ld \n", + vma->vm_pgoff, size); + + if(size > video_buffer_size){ + pr_debug("atmel_isi: mmap requested buffer is to large\n"); + return -EINVAL; + } + if(vma->vm_pgoff > video_buffers){ + pr_debug("atmel_isi: invalid mmap page offset\n"); + return -EINVAL; + } + pfn = isi->video_buffer[vma->vm_pgoff].fb_desc.fb_address >> PAGE_SHIFT; + + ret = remap_pfn_range(vma, vma->vm_start, pfn, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + if(ret){ + return ret; + } + + vma->vm_ops = &avr32_isi_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; /* fixed size */ + vma->vm_flags |= VM_RESERVED;/* do not swap out */ + vma->vm_flags |= VM_DONTCOPY; + vma->vm_flags |= VM_SHARED; + vma->vm_private_data = (void *) buffer; + avr32_isi_vm_open(vma); + + pr_debug("atmel_isi: vma start=0x%08lx, size=%ld phys=%ld \n", + (unsigned long) vma->vm_start, + (unsigned long) vma->vm_end - (unsigned long) vma->vm_start, + pfn << PAGE_SHIFT); + return 0; +} + +static unsigned int avr32_isi_poll(struct file *file, poll_table *wait) +{ + struct atmel_isi_fh *fh = file->private_data; + struct atmel_isi *isi = fh->isi; + unsigned int ret = 0; + + mutex_lock(&isi->mutex); + poll_wait(file, &isi->capture_wq, wait); + if(kfifo_len(isi->doneq)) + ret = POLLIN | POLLRDNORM; + mutex_unlock(&isi->mutex); + + return ret; +} + +static int avr32_isi_stream_close (struct inode *inode, struct file *file) +{ + struct atmel_isi_fh *fh = file->private_data; + struct atmel_isi *isi = fh->isi; + u32 cr1; + + mutex_lock(&isi->mutex); + + isi->stream_users--; + kfree(fh); + + /* Stop camera and ISI if driver has no users */ + if(!isi->capture_users) { + isi->camera->stop_capture(isi->camera); + + spin_lock_irq(&isi->lock); + cr1 = isi_readl(isi, CR1); + cr1 |= ISI_BIT(DIS); + isi_writel(isi, CR1, cr1); + spin_unlock_irq(&isi->lock); + } + + mutex_unlock(&isi->mutex); + + return 0; +} + +static int avr32_isi_stream_open (struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct atmel_isi *isi = to_atmel_isi(vdev); + struct atmel_isi_fh *fh; + int ret = -EBUSY; + unsigned long timeout; + + mutex_lock(&isi->mutex); + + + if (isi->stream_users) { + pr_debug("%s: open(): device busy\n", vdev->name); + goto out; + } + + if (!isi->camera) { + ret = -ENODEV; + isi->camera = avr32_isi_grab_camera(isi); + if (!isi->camera) + goto out; + ret = -EINVAL; + ret = avr32_isi_set_camera_input(isi); + if(ret) + goto out; + } + avr32_isi_streaming_set_format(isi, &isi->format); + kfifo_reset(isi->grabq); + kfifo_reset(isi->doneq); + + /* + * Reset the controller and wait for completion. The + * reset will only succeed if we have a pixel clock + * from the camera. + */ + if(isi->stream_users == 0){ + + init_completion(&isi->reset_complete); + isi_writel(isi, IER, ISI_BIT(SOFTRST)); + isi_writel(isi, CR1, ISI_BIT(RST)); + + timeout = wait_for_completion_timeout(&isi->reset_complete, + msecs_to_jiffies(100)); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto out; + } + isi_writel(isi, IDR, ~0UL); + } + + ret = -ENOMEM; + fh = kzalloc(sizeof(struct atmel_isi_fh), GFP_KERNEL); + if (!fh) { + pr_debug("%s: open(): out of memory\n", vdev->name); + goto out; + } + + fh->isi = isi; + file->private_data = fh; + isi->stream_users++; + + ret = 0; + +out: + mutex_unlock(&isi->mutex); + return ret; +} + +static void avr32_isi_stream_release (struct video_device *vdev) +{ + struct atmel_isi *isi = to_atmel_isi(vdev); + pr_debug("%s: release\n", vdev->name); + kfree(isi); +} + +/* -----------------------------------------------------------------------*/ + +/* Streaming v4l2 device file operations */ +static struct file_operations avr32_isi_streaming_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .open = avr32_isi_stream_open, + .release = avr32_isi_stream_close, + .mmap = avr32_isi_mmap, + .poll = avr32_isi_poll, +}; + +/* Capture v4l2 device file operations */ +static struct file_operations avr32_isi_capture_fops = { + .owner = THIS_MODULE, + .open = avr32_isi_capture_open, + .release = avr32_isi_capture_close, + .read = avr32_isi_capture_read, + .ioctl = video_ioctl2, +}; + +static int __exit avr32_isi_remove(struct platform_device *pdev) +{ + struct atmel_isi *isi = platform_get_drvdata(pdev); + int i; + + if (isi->camera) + isi->camera->stop_capture(isi->camera); + + if (isi->camera) + avr32_isi_release_camera(isi, isi->camera); + video_unregister_device(&isi->cdev); + video_unregister_device(&isi->vdev); + + platform_set_drvdata(pdev, NULL); + + /* release capture buffer */ + dma_free_coherent(&pdev->dev, capture_buffer_size, + isi->capture_buf, isi->capture_phys); + + /* release frame buffers */ + for(i = 0; i < video_buffers; i++){ + dma_free_coherent(&pdev->dev, + video_buffer_size, + isi->video_buffer[i].frame_buffer, + isi->video_buffer[i].fb_desc.fb_address); + } + + kfifo_free(isi->doneq); + kfifo_free(isi->grabq); + + free_irq(isi->irq, isi); + iounmap(isi->regs); + clk_disable(isi->hclk); + clk_disable(isi->pclk); + clk_put(isi->hclk); + clk_put(isi->pclk); + + /* + * Don't free isi here -- it will be taken care of by the + * release() callback. + */ + + return 0; +} + + +static int __init avr32_isi_probe(struct platform_device *pdev) +{ + unsigned int irq; + struct atmel_isi *isi; + struct clk *pclk, *hclk; + struct resource *regs; + int ret; + int i; + int video_bytes_used = video_buffer_size; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(!regs) + return -ENXIO; + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) + return PTR_ERR(pclk); + hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) { + ret = PTR_ERR(hclk); + goto err_hclk; + } + clk_enable(pclk); + clk_enable(hclk); + + isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL); + if(!isi){ + ret = -ENOMEM; + dev_err(&pdev->dev, "can't allocate interface!\n"); + goto err_alloc_isi; + } + + isi->pclk = pclk; + isi->hclk = hclk; + + /* Round up buffer sizes to the next page if needed */ + video_buffer_size = PAGE_ALIGN(video_buffer_size); + capture_buffer_size = PAGE_ALIGN(capture_buffer_size); + + spin_lock_init(&isi->lock); + mutex_init(&isi->mutex); + init_waitqueue_head(&isi->capture_wq); + + /* Initialize v4l2 capture device */ + isi->cdev.fops = &avr32_isi_capture_fops; + strcpy(isi->cdev.name, "atmel_isi_capture"); + isi->cdev.type = VFL_TYPE_GRABBER; + isi->cdev.type2 = VID_TYPE_CAPTURE; + isi->cdev.minor = -1; + isi->cdev.release =avr32_isi_capture_release; + isi->cdev.vidioc_querycap = avr32_isi_capture_querycap; + isi->cdev.vidioc_enum_fmt_cap = avr32_isi_capture_enum_fmt_cap; + isi->cdev.vidioc_try_fmt_cap = avr32_isi_capture_try_fmt_cap; + isi->cdev.vidioc_g_fmt_cap = avr32_isi_capture_g_fmt_cap; + isi->cdev.vidioc_s_fmt_cap = avr32_isi_capture_s_fmt_cap; + isi->cdev.vidioc_enum_input = avr32_isi_capture_enum_input; + isi->cdev.vidioc_g_input = avr32_isi_capture_g_input; + isi->cdev.vidioc_s_input = avr32_isi_capture_s_input; +#ifdef DEBUG + isi->cdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; +#endif + + /* Initialize v4l2 streaming device */ + isi->vdev.fops = &avr32_isi_streaming_fops; + strcpy(isi->vdev.name, "atmel-isi"); + isi->vdev.type = VFL_TYPE_GRABBER; + isi->vdev.type2 = VID_TYPE_CAPTURE; + isi->vdev.minor = -1; + isi->vdev.release = avr32_isi_stream_release; +#ifdef DEBUG + isi->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; +#endif + + isi->vdev.vidioc_querycap = avr32_isi_querycap; + isi->vdev.vidioc_enum_fmt_cap = avr32_isi_enum_fmt_cap; + isi->vdev.vidioc_try_fmt_cap = avr32_isi_try_fmt_cap; + isi->vdev.vidioc_g_fmt_cap = avr32_isi_g_fmt_cap; + isi->vdev.vidioc_s_fmt_cap = avr32_isi_s_fmt_cap; + isi->vdev.vidioc_enum_input = avr32_isi_enum_input; + isi->vdev.vidioc_g_input = avr32_isi_g_input; + isi->vdev.vidioc_s_input = avr32_isi_s_input; + isi->vdev.vidioc_queryctrl = avr32_isi_queryctrl; + isi->vdev.vidioc_g_ctrl = avr32_isi_g_ctrl; + isi->vdev.vidioc_s_ctrl = avr32_isi_s_ctrl; + isi->vdev.vidioc_querybuf = avr32_isi_querybuf; + isi->vdev.vidioc_reqbufs = avr32_isi_reqbufs; + isi->vdev.vidioc_qbuf = avr32_isi_qbuf; + isi->vdev.vidioc_dqbuf = avr32_isi_dqbuf; + isi->vdev.vidioc_streamon = avr32_isi_streamon; + isi->vdev.vidioc_streamoff = avr32_isi_streamoff; + + isi->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!isi->regs) { + ret = -ENOMEM; + goto err_ioremap; + } + + irq = platform_get_irq(pdev,0); + ret = request_irq(irq, isi_interrupt, 0, "isi", isi); + if (ret) { + dev_err(&pdev->dev, "unable to request irq %d\n", irq); + goto err_req_irq; + } + isi->irq = irq; + + /* Allocate ISI capture buffer */ + isi->capture_buf = dma_alloc_coherent(&pdev->dev, + capture_buffer_size, + &isi->capture_phys, + GFP_KERNEL); + if (!isi->capture_buf) { + ret = -ENOMEM; + dev_err(&pdev->dev, "failed to allocate capture buffer\n"); + goto err_alloc_cbuf; + } + + /* Allocate and initialize video buffers */ + for(i=0;i < video_buffers; i++){ + memset(&isi->video_buffer[i], 0, sizeof(struct frame_buffer)); + isi->video_buffer[i].frame_buffer = + dma_alloc_coherent(&pdev->dev, + video_buffer_size, + (dma_addr_t *) + &(isi->video_buffer[i].fb_desc.fb_address), + GFP_KERNEL); + if(!isi->video_buffer[i].frame_buffer){ + ret = -ENOMEM; + dev_err(&pdev->dev, + "failed to allocate video buffer\n"); + goto err_alloc_vbuf; + } + + isi->video_buffer[i].bytes_used = video_bytes_used; + isi->video_buffer[i].status = FRAME_UNUSED; + isi->video_buffer[i].index = i; + +#ifdef DEBUG + /* Put some color into the buffers */ + /* + memset(isi->video_buffer[i].frame_buffer, (i*4)%0xFF, + video_buffer_size); + */ +#endif + } + /* set up frame buffer descriptor list for ISI module*/ + /* FIXME + isi->fbd_list_start = dma_map_single(&pdev->dev, + &isi->video_buffer[0].fb_desc, + sizeof(struct fbd), + DMA_NONE); + */ + isi->fbd_list_start = __pa(&isi->video_buffer[0].fb_desc); + for(i=0; i < (video_buffers - 1); i++){ + isi->video_buffer[i].fb_desc.next_fbd_address = + /* + dma_map_single(&pdev->dev, + &isi->video_buffer[i+1].fb_desc, + sizeof(struct fbd), + DMA_NONE);*/ + __pa(&isi->video_buffer[i+1]); + } + /* FIXME + * isi->video_buffer[i].fb_desc.next_fbd_address = + * isi->fbd_list_start; + */ + isi->video_buffer[i].fb_desc.next_fbd_address = + __pa(&isi->video_buffer[0]); + +#ifdef DEBUG + for(i=0;i < video_buffers; i++){ + pr_debug("atmel_isi: fbd at %08lx video buffer at \ +phys addr %08lx \n", __pa(&isi->video_buffer[i]), + (unsigned long) isi->video_buffer[i].fb_desc.fb_address); + } +#endif + dev_info(&pdev->dev, + "capture buffer: %d bytes at %p (phys 0x%08x)\n", + capture_buffer_size, isi->capture_buf, + isi->capture_phys); + + spin_lock_init(&isi->grabq_lock); + isi->grabq = kfifo_alloc(sizeof(int) * video_buffers, GFP_KERNEL, + &isi->grabq_lock); + if(IS_ERR(isi->grabq)){ + dev_err(&pdev->dev, "fifo allocation failed\n"); + goto err_fifo_alloc1; + } + spin_lock_init(&isi->doneq_lock); + isi->doneq = kfifo_alloc(sizeof(int) * video_buffers, GFP_KERNEL, + &isi->doneq_lock); + if(IS_ERR(isi->doneq)){ + dev_err(&pdev->dev, "fifo allocation failed\n"); + goto err_fifo_alloc2; + } + + isi_writel(isi, CR1, ISI_BIT(DIS)); + + ret = video_register_device(&isi->cdev, VFL_TYPE_GRABBER, -1); + if(ret){ + dev_err(&pdev->dev, "Registering capturing device failed\n"); + goto err_register1; + } + + ret = video_register_device(&isi->vdev, VFL_TYPE_GRABBER, -1); + if (ret){ + dev_err(&pdev->dev, "Registering streaming device failed\n"); + goto err_register2; + } + + platform_set_drvdata(pdev, isi); + + dev_info(&pdev->dev, "Atmel ISI V4L2 device at 0x%08lx\n", + (unsigned long)regs->start); + + return 0; + +err_register2: + video_unregister_device(&isi->cdev); +err_register1: + kfifo_free(isi->doneq); +err_fifo_alloc2: + kfifo_free(isi->grabq); +err_fifo_alloc1: +err_alloc_vbuf: + while(i--) + dma_free_coherent(&pdev->dev, video_buffer_size, + isi->video_buffer[i].frame_buffer, + isi->video_buffer[i].fb_desc.fb_address); + dma_free_coherent(&pdev->dev, capture_buffer_size, + isi->capture_buf, + isi->capture_phys); +err_alloc_cbuf: + free_irq(isi->irq, isi); +err_req_irq: + iounmap(isi->regs); +err_ioremap: + kfree(isi); +err_alloc_isi: + clk_disable(hclk); + clk_disable(pclk); + clk_put(hclk); +err_hclk: + clk_put(pclk); + + return ret; + +} + +static struct platform_driver avr32_isi_driver = { + .probe = avr32_isi_probe, + .remove = __exit_p(avr32_isi_remove), + .driver = { + .name = "atmel_isi", + .owner = THIS_MODULE, + }, +}; + +static int __init avr32_isi_init(void) +{ + + return platform_driver_probe(&avr32_isi_driver, &avr32_isi_probe); + /*if(ret) + return ret; + else + return platform_driver_register(&avr32_isi_driver);*/ +} + + +static void __exit avr32_isi_exit(void) +{ + platform_driver_unregister(&avr32_isi_driver); +} + + +module_init(avr32_isi_init); +module_exit(avr32_isi_exit); + +MODULE_AUTHOR("Lars Häring "); +MODULE_DESCRIPTION("The V4L2 driver for AVR32 Linux"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff --git a/drivers/media/video/atmel-isi.h b/drivers/media/video/atmel-isi.h new file mode 100644 index 0000000..2aa3c14 --- /dev/null +++ b/drivers/media/video/atmel-isi.h @@ -0,0 +1,252 @@ +/* + * Register definitions for the Atmel Image Sensor Interface. + * + * Copyright (C) 2006 Atmel Corporation + * + * This program 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. + */ +#ifndef __ASM_AVR32_ISI_H__ +#define __ASM_AVR32_ISI_H__ + +#include + +/* ISI register offsets */ +#define ISI_CR1 0x0000 +#define ISI_CR2 0x0004 +#define ISI_SR 0x0008 +#define ISI_IER 0x000c +#define ISI_IDR 0x0010 +#define ISI_IMR 0x0014 +#define ISI_PSIZE 0x0020 +#define ISI_PDECF 0x0024 +#define ISI_PPFBD 0x0028 +#define ISI_CDBA 0x002c +#define ISI_Y2R_SET0 0x0030 +#define ISI_Y2R_SET1 0x0034 +#define ISI_R2Y_SET0 0x0038 +#define ISI_R2Y_SET1 0x003c +#define ISI_R2Y_SET2 0x0040 + +/* Bitfields in CR1 */ +#define ISI_RST_OFFSET 0 +#define ISI_RST_SIZE 1 +#define ISI_DIS_OFFSET 1 +#define ISI_DIS_SIZE 1 +#define ISI_HSYNC_POL_OFFSET 2 +#define ISI_HSYNC_POL_SIZE 1 +#define ISI_VSYNC_POL_OFFSET 3 +#define ISI_VSYNC_POL_SIZE 1 +#define ISI_PIXCLK_POL_OFFSET 4 +#define ISI_PIXCLK_POL_SIZE 1 +#define ISI_EMB_SYNC_OFFSET 6 +#define ISI_EMB_SYNC_SIZE 1 +#define ISI_CRC_SYNC_OFFSET 7 +#define ISI_CRC_SYNC_SIZE 1 +#define ISI_FRATE_OFFSET 8 +#define ISI_FRATE_SIZE 3 +#define ISI_FULL_OFFSET 12 +#define ISI_FULL_SIZE 1 +#define ISI_THMASK_OFFSET 13 +#define ISI_THMASK_SIZE 2 +#define ISI_CODEC_ON_OFFSET 15 +#define ISI_CODEC_ON_SIZE 1 +#define ISI_SLD_OFFSET 16 +#define ISI_SLD_SIZE 8 +#define ISI_SFD_OFFSET 24 +#define ISI_SFD_SIZE 8 + +/* Bitfields in CR2 */ +#define ISI_IM_VSIZE_OFFSET 0 +#define ISI_IM_VSIZE_SIZE 11 +#define ISI_GS_MODE_OFFSET 11 +#define ISI_GS_MODE_SIZE 1 +#define ISI_RGB_MODE_OFFSET 12 +#define ISI_RGB_MODE_SIZE 1 +#define ISI_GRAYSCALE_OFFSET 13 +#define ISI_GRAYSCALE_SIZE 1 +#define ISI_RGB_SWAP_OFFSET 14 +#define ISI_RGB_SWAP_SIZE 1 +#define ISI_COL_SPACE_OFFSET 15 +#define ISI_COL_SPACE_SIZE 1 +#define ISI_IM_HSIZE_OFFSET 16 +#define ISI_IM_HSIZE_SIZE 11 +#define ISI_YCC_SWAP_OFFSET 28 +#define ISI_YCC_SWAP_SIZE 2 +#define ISI_RGB_CFG_OFFSET 30 +#define ISI_RGB_CFG_SIZE 2 + +/* Bitfields in SR */ +#define ISI_CDC_STATUS_OFFSET 3 +#define ISI_CDC_STATUS_SIZE 1 + +/* Bitfields in SR/IER/IDR/IMR */ +#define ISI_SOF_OFFSET 0 +#define ISI_SOF_SIZE 1 +#define ISI_SOFTRST_OFFSET 2 +#define ISI_SOFTRST_SIZE 1 +#define ISI_CRC_ERR_OFFSET 4 +#define ISI_CRC_ERR_SIZE 1 +#define ISI_FO_C_OVF_OFFSET 5 +#define ISI_FO_C_OVF_SIZE 1 +#define ISI_FO_P_OVF_OFFSET 6 +#define ISI_FO_P_OVF_SIZE 1 +#define ISI_FO_P_EMP_OFFSET 7 +#define ISI_FO_P_EMP_SIZE 1 +#define ISI_FO_C_EMP_OFFSET 8 +#define ISI_FO_C_EMP_SIZE 1 +#define ISI_FR_OVR_OFFSET 9 +#define ISI_FR_OVR_SIZE 1 + +/* Bitfields in PSIZE */ +#define ISI_PREV_VSIZE_OFFSET 0 +#define ISI_PREV_VSIZE_SIZE 10 +#define ISI_PREV_HSIZE_OFFSET 16 +#define ISI_PREV_HSIZE_SIZE 10 + +/* Bitfields in PCDEF */ +#define ISI_DEC_FACTOR_OFFSET 0 +#define ISI_DEC_FACTOR_SIZE 8 + +/* Bitfields in PPFBD */ +#define ISI_PREV_FBD_ADDR_OFFSET 0 +#define ISI_PREV_FBD_ADDR_SIZE 32 + +/* Bitfields in CDBA */ +#define ISI_CODEC_DMA_ADDR_OFFSET 0 +#define ISI_CODEC_DMA_ADDR_SIZE 32 + +/* Bitfields in Y2R_SET0 */ +#define ISI_Y2R_SET0_C3_OFFSET 24 +#define ISI_Y2R_SET0_C3_SIZE 8 + +/* Bitfields in Y2R_SET1 */ +#define ISI_Y2R_SET1_C4_OFFSET 0 +#define ISI_Y2R_SET1_C4_SIZE 9 +#define ISI_YOFF_OFFSET 12 +#define ISI_YOFF_SIZE 1 +#define ISI_CROFF_OFFSET 13 +#define ISI_CROFF_SIZE 1 +#define ISI_CBOFF_OFFSET 14 +#define ISI_CBOFF_SIZE 1 + +/* Bitfields in R2Y_SET0 */ +#define ISI_C0_OFFSET 0 +#define ISI_C0_SIZE 8 +#define ISI_C1_OFFSET 8 +#define ISI_C1_SIZE 8 +#define ISI_C2_OFFSET 16 +#define ISI_C2_SIZE 8 +#define ISI_ROFF_OFFSET 24 +#define ISI_ROFF_SIZE 1 + +/* Bitfields in R2Y_SET1 */ +#define ISI_R2Y_SET1_C3_OFFSET 0 +#define ISI_R2Y_SET1_C3_SIZE 8 +#define ISI_R2Y_SET1_C4_OFFSET 8 +#define ISI_R2Y_SET1_C4_SIZE 8 +#define ISI_C5_OFFSET 16 +#define ISI_C5_SIZE 8 +#define ISI_GOFF_OFFSET 24 +#define ISI_GOFF_SIZE 1 + +/* Bitfields in R2Y_SET2 */ +#define ISI_C6_OFFSET 0 +#define ISI_C6_SIZE 8 +#define ISI_C7_OFFSET 8 +#define ISI_C7_SIZE 8 +#define ISI_C8_OFFSET 16 +#define ISI_C8_SIZE 8 +#define ISI_BOFF_OFFSET 24 +#define ISI_BOFF_SIZE 1 + +/* Constants for FRATE */ +#define ISI_FRATE_CAPTURE_ALL 0 + +/* Constants for YCC_SWAP */ +#define ISI_YCC_SWAP_DEFAULT 0 +#define ISI_YCC_SWAP_MODE_1 1 +#define ISI_YCC_SWAP_MODE_2 2 +#define ISI_YCC_SWAP_MODE_3 3 + +/* Constants for RGB_CFG */ +#define ISI_RGB_CFG_DEFAULT 0 +#define ISI_RGB_CFG_MODE_1 1 +#define ISI_RGB_CFG_MODE_2 2 +#define ISI_RGB_CFG_MODE_3 3 + +/* Bit manipulation macros */ +#define ISI_BIT(name) \ + (1 << ISI_##name##_OFFSET) +#define ISI_BF(name,value) \ + (((value) & ((1 << ISI_##name##_SIZE) - 1)) \ + << ISI_##name##_OFFSET) +#define ISI_BFEXT(name,value) \ + (((value) >> ISI_##name##_OFFSET) \ + & ((1 << ISI_##name##_SIZE) - 1)) +#define ISI_BFINS(name,value,old) \ + (((old) & ~(((1 << ISI_##name##_SIZE) - 1) \ + << ISI_##name##_OFFSET))\ + | ISI_BF(name,value)) + +/* Register access macros */ +#define isi_readl(port,reg) \ + __raw_readl((port)->regs + ISI_##reg) +#define isi_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + ISI_##reg) + +#define ATMEL_V4L2_VID_FLAGS ( V4L2_CAP_VIDEO_OUTPUT ) + +struct atmel_isi; + +enum atmel_isi_pixfmt { + ATMEL_ISI_PIXFMT_GREY, /* Greyscale */ + ATMEL_ISI_PIXFMT_CbYCrY, + ATMEL_ISI_PIXFMT_CrYCbY, + ATMEL_ISI_PIXFMT_YCbYCr, + ATMEL_ISI_PIXFMT_YCrYCb, + ATMEL_ISI_PIXFMT_RGB24, + ATMEL_ISI_PIXFMT_BGR24, + ATMEL_ISI_PIXFMT_RGB16, + ATMEL_ISI_PIXFMT_BGR16, + ATMEL_ISI_PIXFMT_GRB16, /* G[2:0] R[4:0]/B[4:0] G[5:3] */ + ATMEL_ISI_PIXFMT_GBR16, /* G[2:0] B[4:0]/R[4:0] G[5:3] */ + ATMEL_ISI_PIXFMT_RGB24_REV, + ATMEL_ISI_PIXFMT_BGR24_REV, + ATMEL_ISI_PIXFMT_RGB16_REV, + ATMEL_ISI_PIXFMT_BGR16_REV, + ATMEL_ISI_PIXFMT_GRB16_REV, /* G[2:0] R[4:0]/B[4:0] G[5:3] */ + ATMEL_ISI_PIXFMT_GBR16_REV, /* G[2:0] B[4:0]/R[4:0] G[5:3] */ +}; + +struct atmel_isi_format { + struct v4l2_pix_format pix; + enum atmel_isi_pixfmt input_format; +}; + +struct atmel_isi_camera { + const char *name; + struct module *owner; + struct list_head list; + unsigned int hsync_act_low:1; + unsigned int vsync_act_low:1; + unsigned int pclk_act_falling:1; + unsigned int has_emb_sync:1; + /* ISI supports up to 17 formats */ + unsigned int pixelformats[17]; + int (*get_format)(struct atmel_isi_camera *cam, + struct atmel_isi_format *fmt); + int (*set_format)(struct atmel_isi_camera *cam, + struct atmel_isi_format *fmt); + int (*start_capture)(struct atmel_isi_camera *cam); + int (*stop_capture)(struct atmel_isi_camera *cam); + struct atmel_isi *isi; +}; + +extern int atmel_isi_register_camera(struct atmel_isi_camera *cam); +extern void atmel_isi_unregister_camera(struct atmel_isi_camera *cam); + + +#endif /* __ASM_AVR32_ISI_H__ */ + -- 1.5.4.4