Re: [PATCH] Atmel IMAGE SENSOR INTERFACE (ISI) driver.
Sedji Gaouaou <[email protected]> Fri, 11 Jun 2010 11:27:51 +0200
| Newsgroups | gmane.linux.drivers.video-input-infrastructure,gmane.comp.video.video4linux |
|---|---|
| Message-ID | <[email protected]> |
Hi,
No comments about this patch?
Regards,
Sedji
Le 6/9/2010 4:46 PM, Sedji Gaouaou a écrit :
> Hi,
>
> Here is the first try for the atmel video driver.
> Basically it has 2 video entries:
> 1) One for the capture. It will only allow you to grab one frame
> 2) One for the streaming part.
>
> Memoy allocation needs to be contiguous, and so is done at probe time.
> For the streaming part no actual interupt is needed as the ISI handles link
> list of buffers.
>
> I am still facing some issues with the v4l2_i2c_new_subdev_board function which
> retunrs an error, but as we fix a video format I don't really need to be able
> to set up the sensor format(done at probe time).
>
> This driver will only work or the at91sam9g45 and at91sam9m10 as it is a new
> version of the Image Sensor Interface IP.
>
> Regards,
> Sedji
>
> ---
> drivers/media/video/atmel-isi.c | 1789 +++++++++++++++++++++++++++++++++++++++
> include/media/atmel-isi.h | 460 ++++++++++
> 2 files changed, 2249 insertions(+), 0 deletions(-)
> create mode 100644 drivers/media/video/atmel-isi.c
> create mode 100644 include/media/atmel-isi.h
>
> diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
> new file mode 100644
> index 0000000..1348830
> --- /dev/null
> +++ b/drivers/media/video/atmel-isi.c
> @@ -0,0 +1,1789 @@
> +/*
> + * 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.
> + */
> +
> +#include<linux/clk.h>
> +#include<linux/completion.h>
> +#include<linux/dma-mapping.h>
> +#include<linux/fs.h>
> +#include<linux/init.h>
> +#include<linux/interrupt.h>
> +#include<linux/ioctl.h>
> +#include<linux/kernel.h>
> +#include<linux/mm.h>
> +#include<linux/module.h>
> +#include<linux/moduleparam.h>
> +#include<linux/platform_device.h>
> +#include<linux/slab.h>
> +#include<linux/version.h>
> +#include<linux/wait.h>
> +#include<linux/i2c.h>
> +#include<linux/kfifo.h>
> +#include<linux/time.h>
> +#include<linux/io.h>
> +
> +#include<mach/board.h>
> +#include<mach/cpu.h>
> +
> +#include<media/atmel-isi.h>
> +
> +#define ATMEL_ISI_VERSION KERNEL_VERSION(0, 1, 0)
> +
> +/* Default ISI capture buffer size */
> +#define ISI_CAPTURE_BUFFER_SIZE (800*600*2)
> +/* Default ISI video frame size ie qvga */
> +#define ISI_VIDEO_BUFFER_SIZE (320*240*2)
> +/* Default number of ISI video buffers */
> +/*(if qvga we can use 4 buffers)*/
> +#define ISI_VIDEO_BUFFERS 4
> +/* Interrupt mask for a single capture */
> +#define ISI_V2_CAPTURE_MASK (ISI_BIT(V2_VSYNC) \
> + | ISI_BIT(V2_CXFR_DONE) | ISI_BIT(V2_PXFR_DONE))
> +/* ISI capture buffer size */
> +static int capture_buffer_size = ISI_CAPTURE_BUFFER_SIZE;
> +/* Number of buffers used for streaming video */
> +static int video_buffers = ISI_VIDEO_BUFFERS;
> +static int video_buffer_size = ISI_VIDEO_BUFFER_SIZE;
> +
> +static int input_format = ATMEL_ISI_PIXFMT_CbYCrY;
> +static u8 has_emb_sync, emb_crc_sync, hsync_act_low;
> +static u8 vsync_act_low, pclk_act_falling, isi_full_mode;
> +/* Preview path horizontal size */
> +static int prev_hsize = 320;
> +/* Preview path vertical size */
> +static int prev_vsize = 240;
> +
> +/* Input image horizontal size */
> +static int image_hsize = 320;
> +/* Input image vertical size */
> +static int image_vsize = 240;
> +
> +static struct timeval start_time;
> +/* Frame rate scaler
> + * 1 = capture every second frame
> + * 2 = capture every third frame
> + * ...
> + * */
> +static int frame_rate_scaler;
> +
> +/* 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;
> +
> +/* Declare static vars that will be used as parameters */
> +/* 0<-> dev/video0, 1<-> dev/video1, -1<-> first free */
> +static int video_nr = -1;
> +
> +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_param(video_nr, int, 0444);
> +
> +/* 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;
> +#if defined(CONFIG_ARCH_AT91SAM9G45) || defined(CONFIG_ARCH_AT91SAM9M10)
> + /* DMA Control Register(new: only in HISI2) */
> + u32 dma_ctrl;
> +#endif
> + /* 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];
> + /* 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
> + */
> + int head, tail;
> + /* 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_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;
> +
> + /* v4l2 device */
> + struct device *dev;
> + struct v4l2_device v4l2_dev;
> + /* sub devices */
> + struct v4l2_subdev *sd;
> + /* Video device for capturing */
> + struct video_device cdev;
> + /* Video device for streaming */
> + struct video_device vdev;
> +
> + /* i2c subdevice board info */
> + struct i2c_board_info board_info;
> + struct i2c_adapter *i2c_adapter;
> +
> + 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;
> +};
> +
> +
> +static void atmel_isi_set_default_format(struct atmel_isi *isi)
> +{
> +
> + isi->format.pix.width = (u32)min((u32)2048l, (u32)image_hsize);
> + isi->format.pix.height = (u32)min((u32)2048l, (u32)image_vsize);
> +
> + /* Set capture format if we have explicitely specified one */
> + if (capture_v4l2_fmt) {
> + isi->format.pix.pixelformat = capture_v4l2_fmt;
> + } else {
> + /* Codec path output format */
> + isi->format.pix.pixelformat = V4L2_PIX_FMT_YVYU;
> + }
> +
> + /* The ISI module codec path tries to output YUV 4:2:2
> + * Therefore two pixels will be in a 32bit word */
> + isi->format.pix.bytesperline = ALIGN(isi->format.pix.width * 2, 4);
> + isi->format.pix.sizeimage = isi->format.pix.bytesperline *
> + isi->format.pix.height;
> +
> + pr_debug("set default format: width=%d height=%d\n",
> + isi->format.pix.width, isi->format.pix.height);
> +
> + isi->streaming_format.pix.width = isi->format.pix.width;
> + isi->streaming_format.pix.height = isi->format.pix.height;
> + isi->streaming_format.pix.bytesperline = isi->format.pix.bytesperline;
> + isi->streaming_format.pix.sizeimage = isi->format.pix.sizeimage;
> +
> + /* Set streaming format if we have explicitely specified one */
> + if (streaming_v4l2_fmt) {
> + isi->streaming_format.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
> + * Also the Greyscale format does not fit on top of the V4L2
> + * format but for now we just return it.
> + */
> + if (input_format == ATMEL_ISI_PIXFMT_GREY)
> + isi->streaming_format.pix.pixelformat =
> + V4L2_PIX_FMT_GREY;
> + else
> + isi->streaming_format.pix.pixelformat =
> + V4L2_PIX_FMT_RGB555X;
> + }
> +
> + if (input_format) {
> + isi->format.input_format = input_format;
> + /* Not needed but for completeness*/
> + isi->streaming_format.input_format = input_format;
> + }
> +
> +}
> +
> +static int atmel_isi_init_hardware(struct atmel_isi *isi)
> +{
> + u32 cfg2, cfg1, cr, ctrl;
> +
> + cr = 0;
> + switch (isi->format.input_format) {
> + case ATMEL_ISI_PIXFMT_GREY:
> + cr = ISI_BIT(GRAYSCALE);
> + break;
> + case ATMEL_ISI_PIXFMT_YCrYCb:
> + cr = ISI_BF(V2_YCC_SWAP, 0);
> + break;
> + case ATMEL_ISI_PIXFMT_YCbYCr:
> + cr = ISI_BF(V2_YCC_SWAP, 1);
> + break;
> + case ATMEL_ISI_PIXFMT_CrYCbY:
> + cr = ISI_BF(V2_YCC_SWAP, 2);
> + break;
> + case ATMEL_ISI_PIXFMT_CbYCrY:
> + cr = ISI_BF(YCC_SWAP, 3);
> + break;
> + case ATMEL_ISI_PIXFMT_RGB24:
> + cr = ISI_BIT(V2_COL_SPACE) | ISI_BF(V2_RGB_CFG, 0);
> + break;
> + case ATMEL_ISI_PIXFMT_BGR24:
> + cr = ISI_BIT(V2_COL_SPACE) | ISI_BF(V2_RGB_CFG, 1);
> + break;
> + case ATMEL_ISI_PIXFMT_RGB16:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_MODE)
> + | ISI_BF(V2_RGB_CFG, 0));
> + break;
> + case ATMEL_ISI_PIXFMT_BGR16:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_MODE)
> + | ISI_BF(V2_RGB_CFG, 1));
> + break;
> + case ATMEL_ISI_PIXFMT_GRB16:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_MODE)
> + | ISI_BF(V2_RGB_CFG, 2));
> + break;
> + case ATMEL_ISI_PIXFMT_GBR16:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_MODE)
> + | ISI_BF(V2_RGB_CFG, 3));
> + break;
> + case ATMEL_ISI_PIXFMT_RGB24_REV:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_SWAP)
> + | ISI_BF(V2_RGB_CFG, 0));
> + break;
> + case ATMEL_ISI_PIXFMT_BGR24_REV:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_SWAP)
> + | ISI_BF(V2_RGB_CFG, 1));
> + break;
> + case ATMEL_ISI_PIXFMT_RGB16_REV:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_SWAP)
> + | ISI_BIT(V2_RGB_MODE) | ISI_BF(V2_RGB_CFG, 0));
> + break;
> + case ATMEL_ISI_PIXFMT_BGR16_REV:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_SWAP)
> + | ISI_BIT(V2_RGB_MODE) | ISI_BF(V2_RGB_CFG, 1));
> + break;
> + case ATMEL_ISI_PIXFMT_GRB16_REV:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_SWAP)
> + | ISI_BIT(V2_RGB_MODE) | ISI_BF(V2_RGB_CFG, 2));
> + break;
> + case ATMEL_ISI_PIXFMT_GBR16_REV:
> + cr = (ISI_BIT(V2_COL_SPACE) | ISI_BIT(V2_RGB_SWAP)
> + | ISI_BIT(V2_RGB_MODE) | ISI_BF(V2_RGB_CFG, 3));
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + cfg1 = ISI_BF(V2_EMB_SYNC, (has_emb_sync))
> + | ISI_BF(V2_HSYNC_POL, hsync_act_low)
> + | ISI_BF(V2_VSYNC_POL, vsync_act_low)
> + | ISI_BF(V2_PIXCLK_POL, pclk_act_falling)
> + | ISI_BF(V2_FULL, isi_full_mode);
> +
> + ctrl = ISI_BIT(DIS);
> +
> + isi_writel(isi, V2_CFG1, cfg1);
> + isi_writel(isi, V2_CTRL, ctrl);
> + /* Check if module properly disable */
> + while (isi_readl(isi, V2_STATUS)& ISI_BIT(V2_DIS_DONE))
> + cpu_relax();
> +
> + cfg2 = isi_readl(isi, V2_CFG2);
> + cfg2 |= cr;
> + cfg2 = ISI_BFINS(V2_IM_VSIZE, isi->format.pix.height - 1, cfg2);
> + cfg2 = ISI_BFINS(V2_IM_HSIZE, isi->format.pix.width - 1, cfg2);
> +
> + isi_writel(isi, V2_CFG2, cfg2);
> +
> + return 0;
> +}
> +
> +static int atmel_isi_start_capture(struct atmel_isi *isi)
> +{
> + u32 cr, sr = 0;
> + int ret;
> +
> + spin_lock_irq(&isi->lock);
> + isi->state = STATE_IDLE;
> +
> + /* clear any pending SOF interrupt */
> + sr = isi_readl(isi, V2_STATUS);
> + /*<=> SOF in previous ISI */
> + isi_writel(isi, V2_INTEN, ISI_BIT(V2_VSYNC));
> + isi_writel(isi, V2_CTRL, isi_readl(isi, V2_CTRL) | ISI_BIT(V2_EN));
> +
> + 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");
> +
> + /* Enable */
> + isi_writel(isi, V2_DMA_CHER, ISI_BIT(V2_DMA_C_CH_EN));
> + isi_writel(isi, V2_DMA_C_ADDR, isi->capture_phys);
> +
> + spin_lock_irq(&isi->lock);
> + isi->state = STATE_CAPTURE_WAIT_SOF;
> + /* Check if already in a frame */
> + while (isi_readl(isi, V2_STATUS)& ISI_BIT(V2_CDC))
> + cpu_relax();
> + cr = isi_readl(isi, V2_CTRL);
> + cr |= ISI_BIT(V2_CDC);
> + isi_writel(isi, V2_CTRL, cr);
> + isi_writel(isi, V2_INTEN, ISI_V2_CAPTURE_MASK);
> +
> + spin_unlock_irq(&isi->lock);
> +
> + return 0;
> +}
> +
> +static void atmel_isi_capture_done(struct atmel_isi *isi,
> + int state)
> +{
> + u32 cr;
> +
> + cr = isi_readl(isi, V2_CTRL);
> + cr&= ~ISI_BIT(V2_CDC);
> + isi_writel(isi, V2_CTRL, cr);
> +
> + isi->state = state;
> + wake_up_interruptible(&isi->capture_wq);
> + isi_writel(isi, V2_INTDIS, ISI_V2_CAPTURE_MASK);
> +}
> +
> +static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi,
> + int sequence)
> +{
> + return IRQ_HANDLED;
> +}
> +
> +/* 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;
> +
> + spin_lock(&isi->lock);
> +
> + status = isi_readl(isi, V2_STATUS);
> + mask = isi_readl(isi, V2_INTMASK);
> + pending = status& mask;
> +
> + if (isi->streaming) {
> + if (likely(pending& (ISI_BIT(V2_CXFR_DONE)))) {
> + sequence++;
> + ret = atmel_isi_handle_streaming(isi, sequence);
> + }
> + } else {
> + while (pending) {
> + if (pending&
> + (ISI_BIT(V2_C_OVR) | ISI_BIT(V2_FR_OVR))) {
> + atmel_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(V2_VSYNC) | ISI_BIT(V2_CDC))) {
> + 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;
> + }
> + }
> + if (pending&
> + (ISI_BIT(V2_CXFR_DONE) | ISI_BIT(V2_PXFR_DONE))) {
> + if (isi->state == STATE_CAPTURE_IN_PROGRESS)
> + atmel_isi_capture_done(isi,
> + STATE_CAPTURE_DONE);
> + }
> +
> + if (pending& ISI_BIT(V2_SRST)) {
> + complete(&isi->reset_complete);
> + isi_writel(isi, V2_INTDIS, ISI_BIT(V2_SRST));
> + }
> +
> + status = isi_readl(isi, V2_STATUS);
> + mask = isi_readl(isi, V2_INTMASK);
> + pending = status& mask;
> + ret = IRQ_HANDLED;
> + }
> + }
> + spin_unlock(&isi->lock);
> +
> + return ret;
> +}
> +/* ------------------------------------------------------------------------
> + * IOCTL videoc handling
> + * ----------------------------------------------------------------------*/
> +
> +/* --------Capture ioctls ------------------------------------------------*/
> +/* Device capabilities callback function.
> + */
> +static int atmel_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 atmel_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, "atmel-isi stream", 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 atmel_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 atmel_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 atmel_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 atmel_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 atmel_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 atmel_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 atmel_isi_streaming_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 atmel_isi_streaming_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, "atmel-isi", 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 atmel_isi_streaming_s_input(struct file *file, void *priv,
> + unsigned int index)
> +{
> + if (index != 0)
> + return -EINVAL;
> +
> + return 0;
> +}
> +/* Gets current input device.
> + */
> +static int atmel_isi_streaming_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 atmel_isi_streaming_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");
> +
> + /* The pretended and normal format are already set earlier */
> + fmt->pixelformat = isi->streaming_format.pix.pixelformat;
> +
> + return 0;
> +}
> +static int atmel_isi_streaming_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 atmel_isi_streaming_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 atmel_isi_streaming_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;
> +
> + if (vfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
> + /* Just return the current format as we do not support
> + * format switching */
> + pr_debug("S_FMT: format not supported(only YUV)\n");
> + memcpy(&vfmt->fmt.pix,&isi->streaming_format.pix,
> + sizeof(struct v4l2_pix_format));
> + } else {
> + /* Set the sensor accordingly */
> + memcpy(&isi->format.pix,&vfmt->fmt.pix,
> + sizeof(struct v4l2_pix_format));
> + v4l2_subdev_call(isi->sd, video, s_fmt, vfmt);
> + }
> +
> + return ret;
> +}
> +/* Checks if control is supported in driver
> + * No controls currently supported yet
> + */
> +static int atmel_isi_streaming_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 atmel_isi_streaming_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 atmel_isi_streaming_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 atmel_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 atmel_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 atmel_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, *next_buffer;
> + u32 old_ctrl;
> +
> + 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[isi->tail]);
> + isi->tail++;
> + if (isi->tail == (video_buffers))
> + isi->tail = 0;
> + next_buffer =&(isi->video_buffer[isi->tail]);
> +
> + /* disable fetch on next buff */
> + next_buffer->fb_desc.dma_ctrl&= ~ISI_BIT(V2_DMA_FETCH);
> + buffer->fb_desc.dma_ctrl |= ISI_BIT(V2_DMA_FETCH);
> +
> + /* Restart the ISI transfert if suspended */
> + old_ctrl = isi_readl(isi, V2_DMA_C_CTRL);
> + isi_writel(isi, V2_DMA_C_CTRL, ISI_BIT(V2_DMA_FETCH) | old_ctrl);
> + isi_writel(isi, V2_DMA_CHER, ISI_BIT(V2_DMA_C_CH_EN));
> +
> + mutex_lock(&isi->mutex);
> + buf->flags |= V4L2_BUF_FLAG_QUEUED;
> + buf->flags&= ~V4L2_BUF_FLAG_DONE;
> + buffer->status = FRAME_QUEUED;
> +
> + mutex_unlock(&isi->mutex);
> +
> + return 0;
> +}
> +
> +static int atmel_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;
> + static int sequence;
> +
> + if (unlikely(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
> + return -EINVAL;
> +
> + buffer =&(isi->video_buffer[isi->head]);
> + /* TEST if C_DONE == 1 ie isi transfer */
> + if ((buffer->fb_desc.dma_ctrl& ISI_BIT(V2_DMA_DONE)) == 0) {
> + pr_debug("In dqbuf: Buffer not ready\n");
> + return -EAGAIN;
> + } else {
> + buffer->status = FRAME_DONE;
> + }
> +
> + buffer->fb_desc.dma_ctrl&= ~ISI_BIT(V2_DMA_DONE) ;
> +
> + if (unlikely(buffer->status == FRAME_QUEUED)) {
> + if (isi->streaming == 0)
> + return 0;
> + pr_debug("isi: error, dequeued buffer not ready\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&isi->mutex);
> + buf->index = isi->head;
> + buf->bytesused = buffer->bytes_used;
> + do_gettimeofday(&buf->timestamp);
> + buf->timestamp.tv_sec -= start_time.tv_sec;
> + buf->timestamp.tv_usec -= start_time.tv_usec;
> + buf->sequence = sequence++;
> + buf->m.offset = (isi->head)<< PAGE_SHIFT;
> + buffer->status = FRAME_UNUSED;
> + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
> + buf->length = video_buffer_size;
> + buf->field = V4L2_FIELD_NONE;
> + buf->memory = V4L2_MEMORY_MMAP;
> + mutex_unlock(&isi->mutex);
> +
> + isi->head++;
> + if ((isi->head) == (video_buffers))
> + isi->head = 0;
> +
> + return 0;
> +}
> +static int atmel_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 i;
> + struct frame_buffer *buffer;
> + u32 cfg1, ctrl;
> +
> + if (unlikely(type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
> + return -EINVAL;
> +
> + /* reset ISI transfert desc */
> + for (i = 0; i< (video_buffers - 1); i++)
> + isi->video_buffer[i].fb_desc.dma_ctrl =
> + ISI_BIT(V2_DMA_FETCH) | ISI_BIT(V2_DMA_WB);
> +
> + /* ISI will stop at this point(last buffer of the queue) */
> + isi->video_buffer[i].fb_desc.dma_ctrl = ISI_BIT(V2_DMA_WB);
> +
> + buffer =&(isi->video_buffer[isi->head]);
> +
> + spin_lock_irq(&isi->lock);
> + isi->streaming = 1;
> +
> + ctrl = isi_readl(isi, V2_CTRL);
> + cfg1 = isi_readl(isi, V2_CFG1);
> + /* Disable irq: cxfr for the codec path, pxfr for the preview path */
> + isi_writel(isi, V2_INTDIS,
> + ISI_BIT(V2_CXFR_DONE) | ISI_BIT(V2_PXFR_DONE));
> +
> + /* Enable codec path */
> + ctrl |= ISI_BIT(V2_CDC);
> +
> + /* Write the address of the first frame buffer in the C_ADDR reg
> + * write the address of the first descriptor(link list of buffer)
> + * in the C_DSCR reg, and enable dma channel.
> + */
> + isi_writel(isi, V2_DMA_C_DSCR, (__pa(&(buffer->fb_desc))));
> + isi_writel(isi, V2_DMA_C_CTRL,
> + ISI_BIT(V2_DMA_FETCH) | ISI_BIT(V2_DMA_DONE));
> + isi_writel(isi, V2_DMA_CHER, ISI_BIT(V2_DMA_C_CH_EN));
> +
> + /* Enable linked list */
> + cfg1 |= ISI_BF(V2_FRATE, frame_rate_scaler) | ISI_BIT(V2_DISCR);
> +
> + /* Enable ISI module*/
> + ctrl |= ISI_BIT(V2_ENABLE);
> + isi_writel(isi, V2_CTRL, ctrl);
> + isi_writel(isi, V2_CFG1, cfg1);
> +
> + /* To properly set the timestamp we need to record the time at start
> + * up*/
> + do_gettimeofday(&start_time);
> +
> + spin_unlock_irq(&isi->lock);
> +
> + return 0;
> +}
> +
> +static int atmel_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;
> +
> + /* Disble codec path */
> + isi_writel(isi, V2_CTRL, isi_readl(isi, V2_CTRL)& (~ISI_BIT(V2_CDC)));
> + /* Disable interrupts */
> + isi_writel(isi, V2_INTDIS,
> + ISI_BIT(V2_CXFR_DONE) | ISI_BIT(V2_PXFR_DONE));
> + /* Disable ISI module*/
> + isi_writel(isi, V2_CTRL, isi_readl(isi, V2_CTRL) | ISI_BIT(V2_DIS));
> +
> + spin_unlock_irq(&isi->lock);
> +
> + for (reqnr = 0; reqnr< video_buffers; reqnr++)
> + isi->video_buffer[reqnr].status = FRAME_UNUSED;
> +
> + return 0;
> +}
> +static int atmel_isi_g_parm(struct file *file, void *f,
> + struct v4l2_streamparm *parm)
> +{
> + int err = 0;
> + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + return err;
> +}
> +/*----------------------------------------------------------------------------*/
> +
> +static int atmel_isi_init(struct atmel_isi *isi)
> +{
> + unsigned long timeout;
> +
> + /*
> + * Reset the controller and wait for completion.
> + * The reset will only succeed if we have a
> + * pixel clock from the camera.
> + */
> + init_completion(&isi->reset_complete);
> +
> + isi_writel(isi, V2_INTEN, ISI_BIT(V2_SRST));
> + isi_writel(isi, V2_CTRL, ISI_BIT(V2_SRST));
> +
> + timeout = wait_for_completion_timeout(&isi->reset_complete,
> + msecs_to_jiffies(100));
> + if (timeout == 0)
> + return -ETIMEDOUT;
> +
> + isi_writel(isi, V2_INTDIS, ~0UL);
> +
> + atmel_isi_set_default_format(isi);
> +
> + atmel_isi_init_hardware(isi);
> +
> + return 0;
> +}
> +
> +static int atmel_isi_capture_close(struct file *file)
> +{
> + struct atmel_isi_fh *fh = file->private_data;
> + struct atmel_isi *isi = fh->isi;
> + u32 cr;
> +
> + mutex_lock(&isi->mutex);
> +
> + isi->capture_users--;
> + kfree(fh);
> +
> + /* Stop camera and ISI if driver has no users */
> + if (!isi->stream_users) {
> +
> + spin_lock_irq(&isi->lock);
> +
> + cr = isi_readl(isi, V2_CTRL);
> + cr |= ISI_BIT(V2_DIS);
> + isi_writel(isi, V2_CTRL, cr);
> +
> + spin_unlock_irq(&isi->lock);
> + }
> + mutex_unlock(&isi->mutex);
> +
> + return 0;
> +}
> +
> +static int atmel_isi_capture_open(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;
> +
> + pr_debug("%s: opened\n", isi->cdev.name);
> +
> + mutex_lock(&isi->mutex);
> +
> + if (isi->capture_users) {
> + pr_debug("%s: open(): device busy\n", isi->cdev.name);
> + goto out;
> + }
> +
> + /* If the streaming interface has no users too we do a
> + * init of the hardware and software configuration.
> + */
> + if (isi->stream_users == 0) {
> + ret = atmel_isi_init(isi);
> + if (ret)
> + goto out;
> + }
> +
> + ret = -ENOMEM;
> + fh = kzalloc(sizeof(struct atmel_isi_fh), GFP_KERNEL);
> + if (!fh) {
> + pr_debug("%s: open(): out of memory\n", isi->cdev.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 atmel_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);
> +
> + atmel_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 atmel_isi_capture_release(struct video_device *vdev)
> +{
> + pr_debug("%s: release\n", vdev->name);
> +}
> +/* ----------------- Streaming interface -------------------------------------*/
> +static void atmel_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 atmel_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(KERN_INFO "atmel_isi: mmap_count went negative\n");
> +}
> +
> +static struct vm_operations_struct atmel_isi_vm_ops = {
> + .open = atmel_isi_vm_open,
> + .close = atmel_isi_vm_close,
> +};
> +
> +static int atmel_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 =&atmel_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;
> + atmel_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 int atmel_isi_stream_close(struct file *file)
> +{
> + struct atmel_isi_fh *fh = file->private_data;
> + struct atmel_isi *isi = fh->isi;
> + u32 cr;
> +
> + mutex_lock(&isi->mutex);
> +
> + isi->stream_users--;
> + kfree(fh);
> +
> + /* Stop camera and ISI if driver has no users */
> + if (!isi->capture_users) {
> + spin_lock_irq(&isi->lock);
> + cr = isi_readl(isi, V2_CTRL);
> + cr |= ISI_BIT(V2_DIS);
> + isi_writel(isi, V2_CTRL, cr);
> + spin_unlock_irq(&isi->lock);
> + }
> +
> + mutex_unlock(&isi->mutex);
> +
> + return 0;
> +}
> +
> +static int atmel_isi_stream_open(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;
> +
> + mutex_lock(&isi->mutex);
> + /* Just one user is allowed for the streaming device*/
> + if (isi->stream_users) {
> + pr_debug("%s: open(): device busy\n", vdev->name);
> + goto out;
> + }
> +
> + /* If the capture interface is unused too we do a
> + * init of hardware/software configuration
> + */
> + if (isi->capture_users == 0) {
> + ret = atmel_isi_init(isi);
> + if (ret)
> + 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->stream_users++;
> +
> + ret = 0;
> +
> +out:
> + mutex_unlock(&isi->mutex);
> + return ret;
> +}
> +
> +static void atmel_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 v4l2_file_operations atmel_isi_streaming_fops = {
> + .owner = THIS_MODULE,
> + .ioctl = video_ioctl2,
> + .open = atmel_isi_stream_open,
> + .release = atmel_isi_stream_close,
> + .mmap = atmel_isi_mmap,
> +};
> +/* Capture v4l2 device file operations */
> +static struct v4l2_file_operations atmel_isi_capture_fops = {
> + .owner = THIS_MODULE,
> + .open = atmel_isi_capture_open,
> + .release = atmel_isi_capture_close,
> + .read = atmel_isi_capture_read,
> + .ioctl = video_ioctl2,
> +};
> +
> +static int __exit atmel_isi_remove(struct platform_device *pdev)
> +{
> + struct atmel_isi *isi = platform_get_drvdata(pdev);
> + int i;
> +
> + kfree(&isi->sd);
> + v4l2_device_unregister(&isi->v4l2_dev);
> + 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);
> + }
> +
> + 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 const struct v4l2_ioctl_ops atmel_isi_capture_ioctl_ops = {
> + .vidioc_querycap = atmel_isi_capture_querycap,
> + .vidioc_enum_fmt_vid_cap = atmel_isi_capture_enum_fmt_cap,
> + .vidioc_g_fmt_vid_cap = atmel_isi_capture_g_fmt_cap,
> + .vidioc_try_fmt_vid_cap = atmel_isi_capture_try_fmt_cap,
> + .vidioc_s_fmt_vid_cap = atmel_isi_capture_s_fmt_cap,
> + .vidioc_reqbufs = atmel_isi_reqbufs,
> + .vidioc_querybuf = atmel_isi_querybuf,
> + .vidioc_qbuf = atmel_isi_qbuf,
> + .vidioc_dqbuf = atmel_isi_dqbuf,
> + .vidioc_enum_input = atmel_isi_capture_enum_input,
> + .vidioc_g_input = atmel_isi_capture_g_input,
> + .vidioc_s_input = atmel_isi_capture_s_input,
> +};
> +
> +static struct video_device atmel_isi_capture_template = {
> + .fops =&atmel_isi_capture_fops,
> + .minor = -1,
> + .ioctl_ops =&atmel_isi_capture_ioctl_ops,
> + .current_norm = V4L2_STD_PAL,
> +};
> +static const struct v4l2_ioctl_ops atmel_isi_streaming_ioctl_ops = {
> + .vidioc_querycap = atmel_isi_streaming_querycap,
> + .vidioc_enum_fmt_vid_cap = atmel_isi_streaming_enum_fmt_cap,
> + .vidioc_g_fmt_vid_cap = atmel_isi_streaming_g_fmt_cap,
> + .vidioc_try_fmt_vid_cap = atmel_isi_streaming_try_fmt_cap,
> + .vidioc_s_fmt_vid_cap = atmel_isi_streaming_s_fmt_cap,
> + .vidioc_reqbufs = atmel_isi_reqbufs,
> + .vidioc_querybuf = atmel_isi_querybuf,
> + .vidioc_qbuf = atmel_isi_qbuf,
> + .vidioc_dqbuf = atmel_isi_dqbuf,
> + .vidioc_enum_input = atmel_isi_streaming_enum_input,
> + .vidioc_g_input = atmel_isi_streaming_g_input,
> + .vidioc_s_input = atmel_isi_streaming_s_input,
> + .vidioc_queryctrl = atmel_isi_streaming_queryctrl,
> + .vidioc_g_ctrl = atmel_isi_streaming_g_ctrl,
> + .vidioc_s_ctrl = atmel_isi_streaming_s_ctrl,
> + .vidioc_streamon = atmel_isi_streamon,
> + .vidioc_streamoff = atmel_isi_streamoff,
> + .vidioc_g_parm = atmel_isi_g_parm,
> +};
> +
> +static struct video_device atmel_isi_streaming_template = {
> + .fops =&atmel_isi_streaming_fops,
> + .minor = -1,
> + .ioctl_ops =&atmel_isi_streaming_ioctl_ops,
> + .current_norm = V4L2_STD_PAL,
> +};
> +
> +
> +static int __init atmel_isi_probe(struct platform_device *pdev)
> +{
> + unsigned int irq;
> + struct atmel_isi *isi;
> + struct clk *pclk;
> + struct resource *regs;
> + int ret;
> + int i;
> + int video_bytes_used = video_buffer_size;
> + struct device *dev =&pdev->dev;
> + struct isi_platform_data *pdata;
> + struct i2c_adapter *i2c_adap;
> +
> + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!regs)
> + return -ENXIO;
> +
> + pclk = clk_get(&pdev->dev, "isi_clk");
> + if (IS_ERR(pclk))
> + return PTR_ERR(pclk);
> +
> + clk_enable(pclk);
> +
> + 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;
> + spin_lock_init(&isi->lock);
> + mutex_init(&isi->mutex);
> + init_waitqueue_head(&isi->capture_wq);
> +
> + /* Initialize v4l2 capture device */
> + isi->cdev = atmel_isi_capture_template;
> + isi->cdev.release = atmel_isi_capture_release;
> + strcpy(isi->cdev.name, "atmel_isi_capture");
> +#ifdef DEBUG
> + isi->cdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
> +#endif
> +
> + /* Initialize v4l2 streaming device */
> + isi->vdev = atmel_isi_streaming_template;
> + isi->vdev.release = atmel_isi_stream_release;
> + strcpy(isi->vdev.name, "atmel_isi_streaming");
> +#ifdef DEBUG
> + isi->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
> +#endif
> + isi->regs = ioremap(regs->start, regs->end - regs->start + 1);
> + if (!isi->regs) {
> + ret = -ENOMEM;
> + goto err_ioremap;
> + }
> +
> + if (dev->platform_data) {
> + pdata = (struct isi_platform_data *) dev->platform_data;
> + image_hsize = pdata->image_hsize;
> + image_vsize = pdata->image_vsize;
> +
> + /* load i2c info */
> + isi->board_info = pdata->board_info;
> +
> + if (pdata->prev_hsize)
> + prev_hsize = pdata->prev_hsize;
> + if (pdata->prev_vsize)
> + prev_vsize = pdata->prev_vsize;
> + if (pdata->pixfmt)
> + input_format = pdata->pixfmt;
> + else
> + input_format = ATMEL_ISI_PIXFMT_YCbYCr;
> + frame_rate_scaler = pdata->frate;
> + if (pdata->capture_v4l2_fmt)
> + capture_v4l2_fmt = pdata->capture_v4l2_fmt;
> + if (pdata->streaming_v4l2_fmt)
> + streaming_v4l2_fmt = pdata->streaming_v4l2_fmt;
> + if (pdata->cr1_flags& ISI_HSYNC_ACT_LOW)
> + hsync_act_low = 1;
> + if (pdata->cr1_flags& ISI_VSYNC_ACT_LOW)
> + vsync_act_low = 1;
> + if (pdata->cr1_flags& ISI_PXCLK_ACT_FALLING)
> + pclk_act_falling = 1;
> + if (pdata->cr1_flags& ISI_EMB_SYNC)
> + has_emb_sync = 1;
> + if (pdata->cr1_flags& ISI_CRC_SYNC)
> + emb_crc_sync = 1;
> + if (pdata->cr1_flags& ISI_FULL)
> + isi_full_mode = 1;
> + } else {
> + has_emb_sync = 0;
> + emb_crc_sync = 0;
> + hsync_act_low = 0;
> + vsync_act_low = 0;
> + pclk_act_falling = 0;
> + isi_full_mode = 0;
> + prev_hsize = 320;
> + prev_vsize = 240;
> + image_hsize = 320;
> + image_vsize = 240;
> + dev_info(&pdev->dev,
> + "No config available using default values\n");
> + }
> +
> + video_buffer_size = prev_hsize * prev_vsize * 2;
> + video_bytes_used = video_buffer_size;
> +
> + /* 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);
> +
> + isi_writel(isi, V2_CTRL, ISI_BIT(V2_DIS));
> + /* Check if module disable */
> + while (isi_readl(isi, V2_STATUS)& ISI_BIT(V2_DIS))
> + cpu_relax();
> +
> + 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;
> + }
> +
> + 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 =
> + __pa(&isi->video_buffer[i+1].fb_desc);
> + }
> + isi->video_buffer[i].fb_desc.next_fbd_address =
> + __pa(&isi->video_buffer[0].fb_desc);
> +
> + /* Set head& tail of the TD */
> + isi->head = 0;
> + isi->tail = video_buffers - 1;
> +
> + for (i = 0 ; i< video_buffers; i++) {
> + dev_info(&pdev->dev,
> + "video buffer: %d bytes at %p (phys %08lx)\n",
> + video_buffer_size,
> + isi->video_buffer[i].frame_buffer,
> + (unsigned long) isi->video_buffer[i].fb_desc.fb_address);
> + }
> +
> + dev_info(&pdev->dev,
> + "capture buffer: %d bytes at %p (phys 0x%08x)\n",
> + capture_buffer_size, isi->capture_buf,
> + isi->capture_phys);
> +
> + ret = video_register_device(&isi->cdev, VFL_TYPE_GRABBER, video_nr);
> + if (ret) {
> + dev_err(&pdev->dev, "Registering capturing device failed\n");
> + video_device_release(&isi->cdev);
> + kfree(dev);
> + goto err_register1;
> + }
> +
> + ret = video_register_device(&isi->vdev, VFL_TYPE_GRABBER, video_nr);
> + if (ret) {
> + dev_err(&pdev->dev, "Registering streaming device failed\n");
> + video_device_release(&isi->vdev);
> + kfree(dev);
> + goto err_register2;
> + }
> +
> + ret = v4l2_device_register(dev,&isi->v4l2_dev);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Unable to register v4l2 device.\n");
> + goto err_register_v4l2_device;
> + }
> +
> + platform_set_drvdata(pdev, isi);
> +
> + /* Set the subdev info */
> + i2c_adap = i2c_get_adapter(0);
> + isi->sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
> + /* Load up the subdevice */
> + isi->sd =
> + v4l2_i2c_new_subdev_board(&isi->v4l2_dev,
> + i2c_adap,
> + "atmel_isi",
> + &isi->board_info,
> + NULL);
> + if (isi->sd) {
> + dev_err(&pdev->dev,
> + "v4l2 sub device registered\n");
> + } else {
> + dev_err(&pdev->dev,
> + "v4l2 sub device register fails\n");
> + goto probe_sd_out;
> + }
> +
> + dev_info(&pdev->dev, "Atmel ISI V4L2 device at 0x%08lx\n",
> + (unsigned long)regs->start);
> +
> + return 0;
> +
> +probe_sd_out:
> + kfree(isi->sd);
> +err_register_v4l2_device:
> + v4l2_device_unregister(&isi->v4l2_dev);
> +err_register2:
> + video_unregister_device(&isi->vdev);
> +err_register1:
> + video_unregister_device(&isi->cdev);
> +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);
> +err_alloc_cbuf:
> + dma_free_coherent(&pdev->dev, capture_buffer_size,
> + isi->capture_buf,
> + isi->capture_phys);
> + free_irq(isi->irq, isi);
> +err_req_irq:
> + iounmap(isi->regs);
> +err_ioremap:
> + kfree(isi);
> +err_alloc_isi:
> + clk_disable(pclk);
> +
> + return ret;
> +
> +}
> +
> +static struct platform_driver atmel_isi_driver = {
> + .probe = atmel_isi_probe,
> + .remove = __exit_p(atmel_isi_remove),
> + .driver = {
> + .name = "atmel_isi",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init atmel_isi_init_module(void)
> +{
> + return platform_driver_probe(&atmel_isi_driver,&atmel_isi_probe);
> +}
> +
> +
> +static void __exit atmel_isi_exit(void)
> +{
> + platform_driver_unregister(&atmel_isi_driver);
> +}
> +
> +
> +module_init(atmel_isi_init_module);
> +module_exit(atmel_isi_exit);
> +
> +MODULE_AUTHOR("Lars Häring<[email protected]>");
> +MODULE_DESCRIPTION("The V4L2 driver for atmel Linux");
> +MODULE_LICENSE("GPL");
> +MODULE_SUPPORTED_DEVICE("video");
> diff --git a/include/media/atmel-isi.h b/include/media/atmel-isi.h
> new file mode 100644
> index 0000000..1776fea
> --- /dev/null
> +++ b/include/media/atmel-isi.h
> @@ -0,0 +1,460 @@
> +/*
> + * 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<linux/videodev2.h>
> +#include<linux/i2c.h>
> +
> +#include<media/v4l2-device.h>
> +#include<media/v4l2-common.h>
> +#include<media/v4l2-ioctl.h>
> +
> +/* 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
> +
> +/* ISI_V2 register offsets */
> +#define ISI_V2_CFG1 0x0000
> +#define ISI_V2_CFG2 0x0004
> +#define ISI_V2_PSIZE 0x0008
> +#define ISI_V2_PDECF 0x000c
> +#define ISI_V2_Y2R_SET0 0x0010
> +#define ISI_V2_Y2R_SET1 0x0014
> +#define ISI_V2_R2Y_SET0 0x0018
> +#define ISI_V2_R2Y_SET1 0x001C
> +#define ISI_V2_R2Y_SET2 0x0020
> +#define ISI_V2_CTRL 0x0024
> +#define ISI_V2_STATUS 0x0028
> +#define ISI_V2_INTEN 0x002C
> +#define ISI_V2_INTDIS 0x0030
> +#define ISI_V2_INTMASK 0x0034
> +#define ISI_V2_DMA_CHER 0x0038
> +#define ISI_V2_DMA_CHDR 0x003C
> +#define ISI_V2_DMA_CHSR 0x0040
> +#define ISI_V2_DMA_P_ADDR 0x0044
> +#define ISI_V2_DMA_P_CTRL 0x0048
> +#define ISI_V2_DMA_P_DSCR 0x004C
> +#define ISI_V2_DMA_C_ADDR 0x0050
> +#define ISI_V2_DMA_C_CTRL 0x0054
> +#define ISI_V2_DMA_C_DSCR 0x0058
> +
> +/* 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 CFG1 */
> +#define ISI_V2_HSYNC_POL_OFFSET 2
> +#define ISI_V2_HSYNC_POL_SIZE 1
> +#define ISI_V2_VSYNC_POL_OFFSET 3
> +#define ISI_V2_VSYNC_POL_SIZE 1
> +#define ISI_V2_PIXCLK_POL_OFFSET 4
> +#define ISI_V2_PIXCLK_POL_SIZE 1
> +#define ISI_V2_EMB_SYNC_OFFSET 6
> +#define ISI_V2_EMB_SYNC_SIZE 1
> +#define ISI_V2_CRC_SYNC_OFFSET 7
> +#define ISI_V2_CRC_SYNC_SIZE 1
> +#define ISI_V2_FRATE_OFFSET 8
> +#define ISI_V2_FRATE_SIZE 3
> +#define ISI_V2_DISCR_OFFSET 11
> +#define ISI_V2_DISCR_SIZE 1
> +#define ISI_V2_FULL_OFFSET 12
> +#define ISI_V2_FULL_SIZE 1
> +#define ISI_V2_THMASK_OFFSET 13
> +#define ISI_V2_THMASK_SIZE 2
> +#define ISI_V2_SLD_OFFSET 16
> +#define ISI_V2_SLD_SIZE 8
> +#define ISI_V2_SFD_OFFSET 24
> +#define ISI_V2_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 CFG2 */
> +#define ISI_V2_IM_VSIZE_OFFSET 0
> +#define ISI_V2_IM_VSIZE_SIZE 11
> +#define ISI_V2_GS_MODE_OFFSET 11
> +#define ISI_V2_GS_MODE_SIZE 1
> +#define ISI_V2_RGB_MODE_OFFSET 12
> +#define ISI_V2_RGB_MODE_SIZE 1
> +#define ISI_V2_GRAYSCALE_OFFSET 13
> +#define ISI_V2_GRAYSCALE_SIZE 1
> +#define ISI_V2_RGB_SWAP_OFFSET 14
> +#define ISI_V2_RGB_SWAP_SIZE 1
> +#define ISI_V2_COL_SPACE_OFFSET 15
> +#define ISI_V2_COL_SPACE_SIZE 1
> +#define ISI_V2_IM_HSIZE_OFFSET 16
> +#define ISI_V2_IM_HSIZE_SIZE 11
> +#define ISI_V2_YCC_SWAP_OFFSET 28
> +#define ISI_V2_YCC_SWAP_SIZE 2
> +#define ISI_V2_RGB_CFG_OFFSET 30
> +#define ISI_V2_RGB_CFG_SIZE 2
> +
> +/* Bitfields in CTRL */
> +#define ISI_V2_EN_OFFSET 0
> +#define ISI_V2_EN_SIZE 1
> +#define ISI_V2_DIS_OFFSET 1
> +#define ISI_V2_DIS_SIZE 1
> +#define ISI_V2_SRST_OFFSET 2
> +#define ISI_V2_SRST_SIZE 1
> +#define ISI_V2_CDC_OFFSET 8
> +#define ISI_V2_CDC_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_CDC_STATUS_OFFSET 3
> +#define ISI_CDC_STATUS_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 SR/IER/IDR/IMR(ISI_V2) */
> +#define ISI_V2_ENABLE_OFFSET 0
> +#define ISI_V2_ENABLE_SIZE 1
> +#define ISI_V2_DIS_DONE_OFFSET 1
> +#define ISI_V2_DIS_DONE_SIZE 1
> +#define ISI_V2_SRST_OFFSET 2
> +#define ISI_V2_SRST_SIZE 1
> +#define ISI_V2_CDC_STATUS_OFFSET 8
> +#define ISI_V2_CDC_STATUS_SIZE 1
> +#define ISI_V2_VSYNC_OFFSET 10
> +#define ISI_V2_VSYNC_SIZE 1
> +#define ISI_V2_PXFR_DONE_OFFSET 16
> +#define ISI_V2_PXFR_DONE_SIZE 1
> +#define ISI_V2_CXFR_DONE_OFFSET 17
> +#define ISI_V2_CXFR_DONE_SIZE 1
> +#define ISI_V2_P_OVR_OFFSET 24
> +#define ISI_V2_P_OVR_SIZE 1
> +#define ISI_V2_C_OVR_OFFSET 25
> +#define ISI_V2_C_OVR_SIZE 1
> +#define ISI_V2_CRC_ERR_OFFSET 26
> +#define ISI_V2_CRC_ERR_SIZE 1
> +#define ISI_V2_FR_OVR_OFFSET 27
> +#define ISI_V2_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 PSIZE(ISI_V2) */
> +#define ISI_V2_PREV_VSIZE_OFFSET 0
> +#define ISI_V2_PREV_VSIZE_SIZE 10
> +#define ISI_V2_PREV_HSIZE_OFFSET 16
> +#define ISI_V2_PREV_HSIZE_SIZE 10
> +
> +/* Bitfields in PCDEF */
> +#define ISI_DEC_FACTOR_OFFSET 0
> +#define ISI_DEC_FACTOR_SIZE 8
> +
> +/* Bitfields in PCDEF */
> +#define ISI_V2_DEC_FACTOR_OFFSET 0
> +#define ISI_V2_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 DMA_C_ADDR */
> +#define ISI_V2_DMA_ADDR_OFFSET 0
> +#define ISI_V2_DMA_ADDR_SIZE 32
> +
> +/* Bitfields in DMA_C_CTRL& in DMA_P_CTRL */
> +#define ISI_V2_DMA_FETCH_OFFSET 0
> +#define ISI_V2_DMA_FETCH_SIZE 1
> +#define ISI_V2_DMA_WB_OFFSET 1
> +#define ISI_V2_DMA_WB_SIZE 1
> +#define ISI_V2_DMA_IEN_OFFSET 2
> +#define ISI_V2_DMA_IEN_SIZE 1
> +#define ISI_V2_DMA_DONE_OFFSET 3
> +#define ISI_V2_DMA_DONE_SIZE 1
> +
> +/* Bitfields in DMA_CHER */
> +#define ISI_V2_DMA_P_CH_EN_OFFSET 0
> +#define ISI_V2_DMA_P_CH_EN_SIZE 1
> +#define ISI_V2_DMA_C_CH_EN_OFFSET 1
> +#define ISI_V2_DMA_C_CH_EN_SIZE 1
> +
> +/* Bitfields in Y2R_SET0 */
> +#define ISI_Y2R_SET0_C3_OFFSET 24
> +#define ISI_Y2R_SET0_C3_SIZE 8
> +
> +/* Bitfields in Y2R_SET0(ISI_V2) */
> +#define ISI_V2_Y2R_SET0_C3_OFFSET 24
> +#define ISI_V2_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 Y2R_SET1(ISI_V2) */
> +#define ISI_V2_Y2R_SET1_C4_OFFSET 0
> +#define ISI_V2_Y2R_SET1_C4_SIZE 9
> +#define ISI_V2_YOFF_OFFSET 12
> +#define ISI_V2_YOFF_SIZE 1
> +#define ISI_V2_CROFF_OFFSET 13
> +#define ISI_V2_CROFF_SIZE 1
> +#define ISI_V2_CBOFF_OFFSET 14
> +#define ISI_V2_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_SET0(ISI_V2) */
> +#define ISI_V2_C0_OFFSET 0
> +#define ISI_V2_C0_SIZE 8
> +#define ISI_V2_C1_OFFSET 8
> +#define ISI_V2_C1_SIZE 8
> +#define ISI_V2_C2_OFFSET 16
> +#define ISI_V2_C2_SIZE 8
> +#define ISI_V2_ROFF_OFFSET 24
> +#define ISI_V2_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_SET1(ISI_V2) */
> +#define ISI_V2_R2Y_SET1_C3_OFFSET 0
> +#define ISI_V2_R2Y_SET1_C3_SIZE 8
> +#define ISI_V2_R2Y_SET1_C4_OFFSET 8
> +#define ISI_V2_R2Y_SET1_C4_SIZE 8
> +#define ISI_V2_C5_OFFSET 16
> +#define ISI_V2_C5_SIZE 8
> +#define ISI_V2_GOFF_OFFSET 24
> +#define ISI_V2_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
> +
> +/* Bitfields in R2Y_SET2(ISI_V2) */
> +#define ISI_V2_C6_OFFSET 0
> +#define ISI_V2_C6_SIZE 8
> +#define ISI_V2_C7_OFFSET 8
> +#define ISI_V2_C7_SIZE 8
> +#define ISI_V2_C8_OFFSET 16
> +#define ISI_V2_C8_SIZE 8
> +#define ISI_V2_BOFF_OFFSET 24
> +#define ISI_V2_BOFF_SIZE 1
> +
> +/* Constants for FRATE */
> +#define ISI_FRATE_CAPTURE_ALL 0
> +
> +/* Constants for FRATE(ISI_V2) */
> +#define ISI_V2_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 YCC_SWAP(ISI_V2) */
> +#define ISI_V2_YCC_SWAP_DEFAULT 0
> +#define ISI_V2_YCC_SWAP_MODE_1 1
> +#define ISI_V2_YCC_SWAP_MODE_2 2
> +#define ISI_V2_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
> +
> +/* Constants for RGB_CFG(ISI_V2) */
> +#define ISI_V2_RGB_CFG_DEFAULT 0
> +#define ISI_V2_RGB_CFG_MODE_1 1
> +#define ISI_V2_RGB_CFG_MODE_2 2
> +#define ISI_V2_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 isi_platform_data {
> + u16 image_hsize;
> + u16 image_vsize;
> + u16 prev_hsize;
> + u16 prev_vsize;
> + u16 cr1_flags;
> +#define ISI_HSYNC_ACT_LOW 0x01
> +#define ISI_VSYNC_ACT_LOW 0x02
> +#define ISI_PXCLK_ACT_FALLING 0x04
> +#define ISI_EMB_SYNC 0x08
> +#define ISI_CRC_SYNC 0x10
> +#define ISI_FULL 0x20
> + u8 gs_mode;
> +#define ISI_GS_2PIX_PER_WORD 0x00
> +#define ISI_GS_1PIX_PER_WORD 0x01
> + u8 pixfmt;
> + u8 sfd;
> + u8 sld;
> + u8 thmask;
> +#define ISI_BURST_4_8_16 0x00
> +#define ISI_BURST_8_16 0x01
> +#define ISI_BURST_16 0x02
> + u8 frate;
> +#define ISI_FRATE_DIV_2 0x01
> +#define ISI_FRATE_DIV_3 0x02
> +#define ISI_FRATE_DIV_4 0x03
> +#define ISI_FRATE_DIV_5 0x04
> +#define ISI_FRATE_DIV_6 0x05
> +#define ISI_FRATE_DIV_7 0x06
> +#define ISI_FRATE_DIV_8 0x07
> + int capture_v4l2_fmt;
> + int streaming_v4l2_fmt;
> + /* i2c needed for subdev struct */
> + struct i2c_board_info board_info;
> +};
> +
> +#endif /* __ASM_AVR32_ISI_H__ */
> +