diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 441b877..3a85a5e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -298,6 +298,9 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ +/* Byte-swapped YUYV */ +#define V4L2_PIX_FMT_VYUY v4l2_fourcc('V','Y','U','Y') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y','V','Y','U') /* 16 YUV 4:2:2 */ /* * F O R M A T E N U M E R A T I O N */ diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 44ccaed..78e38d0 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -77,6 +77,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..a53a3c0 --- /dev/null +++ b/drivers/media/video/atmel-isi.c @@ -0,0 +1,1868 @@ +/* + * 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)) + return -EINVAL; + if(unlikely(buf->memory != V4L2_MEMORY_MMAP)) + return -EINVAL; + + buffer = &(isi->video_buffer[buf->index]); + if(unlikely(buffer->status != FRAME_UNUSED)) + 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))){ + return -EBUSY; + } + buffer = &(isi->video_buffer[reqnr]); + + if(unlikely(buffer->status != FRAME_DONE)){ + 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; + + 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); + 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 = to_atmel_isi(vdev); + struct atmel_isi_fh *fh; + int ret = -EBUSY; + unsigned long timeout; + + 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"); +} + +/* FIXME remove this function +struct page *avr32_isi_vm_nopage( struct vm_area_struct *vma, + unsigned long address, int *type ) +{ + return NOPAGE_SIGBUS; +} +*/ + +static struct vm_operations_struct avr32_isi_vm_ops = { + .open = avr32_isi_vm_open, + .close = avr32_isi_vm_close, + //.nopage = avr32_isi_vm_nopage, +}; + +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) + goto err_register1; + + ret = video_register_device(&isi->vdev, VFL_TYPE_GRABBER, -1); + if (ret) + 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); + +/*FIXME 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__ */ + diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 7558484..a8cdf42 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -13,6 +13,20 @@ 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 + +config VIDEO_MT9M112 + tristate "Micron MT9M112 camera" + default n + depends on VIDEO_AVR32_ISI && I2C + ---help--- + This will add support for the Micron MT9M112 camera. + 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 78e38d0..3156969 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -77,7 +77,8 @@ 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_MT9M112) += tm13m3.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_BUF) += video-buf.o diff --git a/drivers/media/video/tm13m3.c b/drivers/media/video/tm13m3.c new file mode 100644 index 0000000..42f0fd3 --- /dev/null +++ b/drivers/media/video/tm13m3.c @@ -0,0 +1,631 @@ +/* + * Micron Mt9M112 camera driver. + * + * Copyright (C) 2005-2007 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. + */ +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "atmel-isi.h" + +/* camera standby pin */ +#define CAM_STANDBY GPIO_PIN_PA(9) +/* camera reset pin */ +#define CAM_RESET GPIO_PIN_PA(8) + +/*! Maximum number of pixels in a row */ +#define TM13M3_MAX_WIDTH 1280 +/*! Maximum number of rows */ +#define TM13M3_MAX_HEIGHT 1024 + +/*! Clock for image sensor CLKIN signal */ +static char mclk_name[32] = "gclk0"; +/*! Parent clock of gclk0 + * Either osc0 or pll0 + * We use osc0 with 20MHz quarz. + */ +static char mclk_parent_name[32] = "osc0"; + +static struct clk *mclk; +static struct clk *mclk_parent; +module_param_string(mclk, mclk_name, sizeof(mclk_name), 0644); +MODULE_PARM_DESC(mclk, "Name of the clock used as camera clock input"); + +module_param_string(mclk_parent, mclk_parent_name, + sizeof(mclk_parent_name), 0644); +MODULE_PARM_DESC(mclk, "Name of mclk parent clock"); + + +/* Register adresses */ +#define CHIP_VERSION 0x0 +#define PROGRAM_CONTROL 0x2CC +#define READ_MODE_CONTEXT_B 0x20 +#define CONTEXT_CONTROL 0xC8 +#define COLUMN_WIDTH 0x4 +#define HORIZONTAL_OUTPUT_SIZE_B 0x1A1 +#define VERTICAL_OUTPUT_SIZE_B 0x1A4 +#define ROW_WIDTH 0x3 +#define PAGE_MAP 0xF0 +#define OUTPUT_FORMAT_CONTROL_A 0x13A +#define HORIZONTAL_ZOOM 0x1A6 +#define VERTICAL_ZOOM 0x1A9 +#define PLL_CONTROL_1 0x66 +#define PLL_CONTROL_2 0x67 +#define CLOCK_CONTROL 0x65 + +/* Chip ID stored in CHIP_VERSION register */ +#define MT9M112_CHIP_ID 0x148C +/* I2C address of camera module */ +#define I2C_TM13M3 0x5D + +static unsigned short normal_i2c[] = { + I2C_TM13M3, + I2C_CLIENT_END +}; +I2C_CLIENT_INSMOD; + +#ifdef CONFIG_DEBUG_FS +struct reg_dbg { + struct tm13m3 *is; + struct dentry *dentry; + unsigned int offset; +}; +#endif + +struct tm13m3 { + struct mutex mutex; + u16 current_page; + u32 current_format; + u16 pll_avr_ctrl; + struct clk *mclk; + struct i2c_client client; + struct atmel_isi_camera cam; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; + struct reg_dbg debugfs_reg[37]; +#endif +}; + + +#define to_tm13m3(cam) container_of(cam, struct tm13m3, cam) + +static struct i2c_driver tm13m3_driver; + +static int tm13m3_write_16(struct tm13m3 *is, u16 reg, u16 value) +{ + int ret = 0; + u16 register_page = 0; + + register_page = reg >> 8; + + if ((register_page != is->current_page) + && (reg != PAGE_MAP)){ + + if( 0 <= (ret = i2c_smbus_write_word_data(&is->client, PAGE_MAP, cpu_to_le16(register_page)))) + is->current_page = register_page; + } + + if(ret >= 0){ + ret = i2c_smbus_write_word_data(&is->client, (u8) reg, cpu_to_le16(value)); + } + return ret; +} + +static int tm13m3_read_16(struct tm13m3 *is, u16 reg) +{ + int ret = 0; + u16 register_page = 0; + + register_page = reg >> 8; + + if ((register_page != is->current_page) + && (reg != PAGE_MAP)){ + + if( 0 <= (ret = i2c_smbus_write_word_data(&is->client, PAGE_MAP, cpu_to_le16(register_page)))) + is->current_page = register_page; + } + +if(ret >= 0){ + ret = i2c_smbus_read_word_data(&is->client, (u8) reg); + } + + if (ret < 0) + return -EIO; + + return le16_to_cpu(ret); +} + +#ifdef CONFIG_DEBUG_FS +#include +#include + +struct tm13m3_reg { + u16 address; + const char *name; +}; + +static struct tm13m3_reg tm13m3_registers[38] = { + { .address = CHIP_VERSION, .name = "chip_version"}, + { .address = 0x1, .name = "row_start"}, + { .address = 0x2, .name = "column_start"}, + { .address = 0x3, .name = "row_width"}, + { .address = 0x4, .name = "column_width"}, + { .address = 0x5, .name = "horizontal_blanking_b"}, + { .address = 0x6, .name = "vertical_blanking_b"}, + { .address = 0x7, .name = "horizontal_blanking_a"}, + { .address = 0x8, .name = "vertical_blanking_a"}, + { .address = 0x0D, .name = "reset"}, + { .address = 0x20, .name = "read_mode_context_b"}, + { .address = 0x21, .name = "read_mode_context_a"}, + { .address = 0x22, .name = "dark_col_row"}, + { .address = 0x65, .name = "clock_control"}, + { .address = 0x66, .name = "pll_control_1"}, + { .address = 0x67, .name = "pll_control_2"}, + { .address = 0xC8, .name = "context_control"}, + { .address = 0x106, .name = "mode_control"}, + { .address = 0x108, .name = "format_control"}, + { .address = 0x13A, .name = "output_format_control_a"}, + { .address = 0x148, .name = "test_pattern_generator"}, + { .address = 0x19B, .name = "output_format_control_b"}, + { .address = 0x1A1, .name = "horizontal_output_size_b"}, + { .address = 0x1A4, .name = "vertical_output_size_b"}, + { .address = 0x1A5, .name = "horizontal_pan"}, + { .address = 0x1A6, .name = "horizontal_zoom"}, + { .address = 0x1A7, .name = "horizontal_output_size_a"}, + { .address = 0x1A8, .name = "vertical_pan"}, + { .address = 0x1A9, .name = "vertical_zoom"}, + { .address = 0x1AA, .name = "vertical_output_size_a"}, + { .address = 240, .name = "page_map"}, + { .address = 0x2C8, .name = "global_context_control"}, + { .address = 0x2CB, .name = "program_advance"}, + { .address = 0x2CC, .name = "program_control"}, + { .address = 0x2D2, .name = "default_program_conf"}, + { .address = 0x2D3, .name = "user_global_context_control"}, + { .address = (0x100 | 0), .name = "module_id"}, + { .address = (0x200 | 2), .name = "mode_control"}, +}; + +static u64 reg_dbg_get(void *data) +{ + struct reg_dbg *reg = data; + int ret = 0; + + mutex_lock(®->is->mutex); + ret = tm13m3_read_16(reg->is, tm13m3_registers[reg->offset].address); + mutex_unlock(®->is->mutex); + + if (ret < 0) { + printk("%s: failed to read reg 0x%02x: %d\n", + reg->is->cam.name, + tm13m3_registers[reg->offset].address, ret); + return ~0ULL; + } + return ret; +} + +static void reg_dbg_set(void *data, u64 val) +{ + struct reg_dbg *reg = data; + int ret = 0; + + mutex_lock(®->is->mutex); + ret = tm13m3_write_16(reg->is, tm13m3_registers[reg->offset].address, (u16) val); + mutex_unlock(®->is->mutex); + + if (ret < 0){ + printk("%s: failed to write reg 0x%02x: %d\n", + reg->is->cam.name, + tm13m3_registers[reg->offset].address, ret); + } +} +DEFINE_SIMPLE_ATTRIBUTE(reg_dbg_fops, reg_dbg_get, reg_dbg_set, "%04llx\n"); + +static void tm13m3_init_debugfs(struct tm13m3 *is) +{ + struct dentry *root, *reg; + unsigned int i; + + root = debugfs_create_dir(is->cam.name, NULL); + if (IS_ERR(root) || !root) + goto err_root; + is->debugfs_root = root; + + for (i = 0; i < ARRAY_SIZE(is->debugfs_reg); i++) { + if (!tm13m3_registers[i].name) + continue; + + is->debugfs_reg[i].is = is; + is->debugfs_reg[i].offset = i; + + reg = debugfs_create_file(tm13m3_registers[i].name, S_IRUGO | S_IWUSR, + root, &is->debugfs_reg[i], + ®_dbg_fops); + if (!reg) + goto err_reg; + is->debugfs_reg[i].dentry = reg; + } + + return; + +err_reg: + while (i--) + debugfs_remove(is->debugfs_reg[i].dentry); + debugfs_remove(root); +err_root: + is->debugfs_root = NULL; + printk(KERN_ERR "%s: failed to initialize debugfs\n", + is->cam.name); +} + +static void tm13m3_cleanup_debugfs(struct tm13m3 *is) +{ + unsigned int i; + + if (is->debugfs_root) { + for (i = 0; i < ARRAY_SIZE(is->debugfs_reg); i++) + debugfs_remove(is->debugfs_reg[i].dentry); + debugfs_remove(is->debugfs_root); + } +} +#else +static inline void tm13m3_init_debugfs(struct tm13m3 *is) +{ + +} + +static inline void tm13m3_cleanup_debugfs(struct tm13m3 *is) +{ + +} +#endif /* CONFIG_DEBUG_FS */ + +static int tm13m3_get_format(struct atmel_isi_camera *cam, + struct atmel_isi_format *fmt) +{ + struct tm13m3 *is = to_tm13m3(cam); + int ret = 0; + + fmt->pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->input_format = ATMEL_ISI_PIXFMT_CbYCrY; + fmt->input_format = is->current_format; + + fmt->pix.width = 320; + fmt->pix.height = 240; + + return ret; +} + +static int tm13m3_set_format(struct atmel_isi_camera *cam, + struct atmel_isi_format *fmt) +{ + struct tm13m3 *is = to_tm13m3(cam); + int ret = 0; + + fmt->pix.colorspace = V4L2_COLORSPACE_SMPTE170M; +/* + switch(fmt->input_format){ + case ATMEL_ISI_PIXFMT_CbYCrY: + is->current_format = ATMEL_ISI_PIXFMT_CbYCrY; + break; + case ATMEL_ISI_PIXFMT_YCbYCr: + is->current_format = ATMEL_ISI_PIXFMT_YCbYCr; + break; + case ATMEL_ISI_PIXFMT_CrYCbY: + is->current_format = ATMEL_ISI_PIXFMT_CrYCbY; + break; + case ATMEL_ISI_PIXFMT_YCrYCb: + is->current_format = ATMEL_ISI_PIXFMT_YCrYCb; + break; + default: + // force a valid format + fmt->input_format = ATMEL_ISI_PIXFMT_CbYCrY; + is->current_format = ATMEL_ISI_PIXFMT_CbYCrY; + pr_debug("%s: Not supported format, forcing default format\n", + cam->name); + break; + } +*/ + fmt->input_format = ATMEL_ISI_PIXFMT_CrYCbY; + is->current_format = ATMEL_ISI_PIXFMT_CrYCbY; + + /* adjust picture width and height */ + if (fmt->pix.width > TM13M3_MAX_WIDTH) + fmt->pix.width = TM13M3_MAX_WIDTH; + if (fmt->pix.height > TM13M3_MAX_HEIGHT) + fmt->pix.height = TM13M3_MAX_HEIGHT; + + //tm13m3_write_16(is, COLUMN_WIDTH, fmt->pix.width); + //tm13m3_write_16(is, ROW_WIDTH, fmt->pix.height); + //tm13m3_write_16(is, HORIZONTAL_ZOOM, fmt->pix.width); + //tm13m3_write_16(is, VERTICAL_ZOOM, fmt->pix.height); + + //tm13m3_write_16(is, HORIZONTAL_OUTPUT_SIZE_B, fmt->pix.width); + //tm13m3_write_16(is, VERTICAL_OUTPUT_SIZE_B, fmt->pix.height); + + /* FIXME Set context output width needed ??*/ + + pr_debug("%s: set_format %ux%u\n", cam->name, + fmt->pix.width, fmt->pix.height); + return ret; +} + +static void tm13m3_reset_soft(struct tm13m3 *is) +{ + tm13m3_write_16(is, 0x0D, 0x0001); + /*FIXME test if toggling is really needed */ + tm13m3_write_16(is, 0x0D, 0x0000); +} + +static void tm13m3_reset_hardware(struct tm13m3 *is) +{ + gpio_set_value(CAM_RESET, 0); + //FIXME : set correct reset interval usleep(); + gpio_set_value(CAM_RESET, 1); +} + +static int tm13m3_start_capture(struct atmel_isi_camera *cam) +{ + struct tm13m3 *is = to_tm13m3(cam); + int ret = 0; + + return ret; +} + +static int tm13m3_stop_capture(struct atmel_isi_camera *cam) +{ + struct tm13m3 *is = to_tm13m3(cam); + int ret = 0; + + + return ret; +} + +static int tm13m3_init_hardware(struct tm13m3 *is) +{ + int chip_id; + + tm13m3_reset_hardware(is); + /* set register page to reset value*/ + is->current_page = 0; + is->current_format = ATMEL_ISI_PIXFMT_CbYCrY; + + pr_debug("tm13m3: Init sensor\n"); + /* Try to identify the camera */ + chip_id = tm13m3_read_16(is, CHIP_VERSION); + if (chip_id < 0) + return -EIO; + + if (chip_id != MT9M112_CHIP_ID) { + printk(KERN_ERR "%s: Unknown chip ID 0x%04x\n", + is->cam.name, chip_id); + return -ENODEV; + } +#if 0 + /* Configure pll for 36,8 MHz with CLKIN = 20MHz + * fout = fclkin * M * 1 /( 2* (N+1) * (P+1)) + */ + /* Set P = 2 */ + tm13m3_write_16(is, PLL_CONTROL_2, 0x0502); + /* M = 22, N = 1*/ + tm13m3_write_16(is, PLL_CONTROL_1, 0x1601); + /* wake up pll*/ + tm13m3_write_16(is, CLOCK_CONTROL, 0x8000); + /* wait until pll has stabilized */ + mdelay(1); + /* set pll as master clock*/ + tm13m3_write_16(is, CLOCK_CONTROL, 0x0000); + +#endif + /* Set semi-auto mode program mode*/ + tm13m3_write_16(is, PROGRAM_CONTROL, 0x0010); + /* set context B read mode */ + tm13m3_write_16(is, READ_MODE_CONTEXT_B, 0x0100); + /* switch to read+resize context B */ + tm13m3_write_16(is, CONTEXT_CONTROL, 0x0408); + /* set ITU-R BT.656 codes */ + tm13m3_write_16(is, OUTPUT_FORMAT_CONTROL_A, 0x0A00); + /* set sensor image size */ + tm13m3_write_16(is, HORIZONTAL_ZOOM, 320); + tm13m3_write_16(is, VERTICAL_ZOOM, 240); + tm13m3_write_16(is, HORIZONTAL_OUTPUT_SIZE_B, 320); + tm13m3_write_16(is, VERTICAL_OUTPUT_SIZE_B, 240); + tm13m3_write_16(is, COLUMN_WIDTH, 320); + tm13m3_write_16(is, ROW_WIDTH, 240); + return 0; +} +static int tm13m3_detect_client(struct i2c_adapter *adapter, + int address, int kind) +{ + struct i2c_client *client; + struct tm13m3 *is; + int ret; + + pr_debug("tm13m3: detecting client on address 0x%x\n", address); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, + (I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_WRITE_WORD_DATA))) + return 0; + + is = kzalloc(sizeof(struct tm13m3), GFP_KERNEL); + if (!is) + return -ENOMEM; + + client = &is->client; + client->addr = address; + client->adapter = adapter; + client->driver = &tm13m3_driver; + strcpy(client->name, "tm13m3"); + + is->cam.name = client->name; + is->cam.hsync_act_low = 0; + is->cam.vsync_act_low = 0; + is->cam.pclk_act_falling = 0; + /* no SAV/EAV sync -> HSYNC and VSYNC used */ + /*is->cam.has_emb_sync = 0;*/ + is->cam.has_emb_sync = 1; + + is->cam.get_format = tm13m3_get_format; + is->cam.set_format = tm13m3_set_format; + is->cam.start_capture = tm13m3_start_capture; + is->cam.stop_capture = tm13m3_stop_capture; + + mutex_init(&is->mutex); + + is->mclk = clk_get(NULL, mclk_name); + if (IS_ERR(is->mclk)) { + ret = PTR_ERR(is->mclk); + goto err_clk; + } + clk_enable(is->mclk); + + ret = i2c_attach_client(client); + if (ret) + goto err_attach; + + i2c_set_clientdata(client, is); + + ret = tm13m3_init_hardware(is); + if (ret) + goto err_init_hw; + + /* We're up and running. Notify the ISI driver */ + ret = atmel_isi_register_camera(&is->cam); + if (ret) + goto err_register; + + printk(KERN_INFO "TM13M3 Image Sensor at %s:0x%02x\n", + adapter->name, address); + + tm13m3_init_debugfs(is); + + return 0; + +err_register: +err_init_hw: +// at76_reset_hardware(is); + i2c_detach_client(client); +err_attach: + clk_disable(is->mclk); + clk_put(is->mclk); +err_clk: + kfree(is); + return ret; +} + +static int tm13m3_attach_adapter(struct i2c_adapter *adapter) +{ + pr_debug("tm13m3: starting probe for adapter %s (%u)\n", + adapter->name, adapter->id); + return i2c_probe(adapter, &addr_data, &tm13m3_detect_client); +} + +static int tm13m3_detach_client(struct i2c_client *client) +{ + struct tm13m3 *is = i2c_get_clientdata(client); + int ret; + + tm13m3_cleanup_debugfs(is); + atmel_isi_unregister_camera(&is->cam); + + tm13m3_reset_hardware(is); + + ret = i2c_detach_client(client); + if (ret) + return ret; + + clk_disable(is->mclk); + clk_put(is->mclk); + kfree(is); + + return 0; +} + +static struct i2c_driver tm13m3_driver = { + .driver = { + .name = "tm13m3", + }, + .id = I2C_DRIVERID_TM13M3, + .attach_adapter = &tm13m3_attach_adapter, + .detach_client = &tm13m3_detach_client, +}; + +static int __init tm13m3_init(void) +{ + /* + * Set up the master clock, if available. If clk_get() fails, + * this hopefully means that the board generates a suitable + * master clock some other way, which is fine by us. + * + * We need to do this before probing the i2c bus, as the + * camera won't ack any messages when it doesn't have a clock. + */ + mclk_parent = clk_get(NULL, mclk_parent_name); + if (!IS_ERR(mclk_parent)) + clk_enable(mclk_parent); + else { + mclk_parent = NULL; + pr_debug("tm13m3: No parent clock available\n"); + } + + mclk = clk_get(NULL, mclk_name); + if (!IS_ERR(mclk)) { + if (mclk_parent) + clk_set_parent(mclk, mclk_parent); + + clk_set_rate(mclk, 27000000); + clk_enable(mclk); + } else { + mclk = NULL; + pr_debug("tm13m3: No clock set\n"); + } + + gpio_direction_output(CAM_STANDBY, 0); + /* Reset sequence */ + gpio_direction_output(CAM_RESET, 0); + udelay(4); + gpio_set_value(CAM_RESET, 1); + + return i2c_add_driver(&tm13m3_driver); + +} +module_init(tm13m3_init); + +static void __exit tm13m3_exit(void) +{ + if (mclk) { + clk_disable(mclk); + clk_put(mclk); + } + if (mclk_parent) { + clk_disable(mclk_parent); + clk_put(mclk_parent); + } + i2c_del_driver(&tm13m3_driver); +} +module_exit(tm13m3_exit); + +MODULE_DESCRIPTION("Atmel Image Sensor Interface Driver"); +MODULE_AUTHOR("Lars Häring "); +MODULE_LICENSE("GPL"); >From dc4286f6020df0bf791228cdfe7d4ea58e2f46ef Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 17 Jan 2007 13:47:40 +0100 Subject: [PATCH] AP7000: Add platform_device for ISI Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/at32ap700x.c | 47 +++++++++++++++++++++++++++++++++ include/asm-avr32/arch-at32ap/board.h | 1 + 2 files changed, 48 insertions(+), 0 deletions(-) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 1130c8a..4184296 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1396,6 +1396,51 @@ at32_add_device_abdac(unsigned int id) } /* -------------------------------------------------------------------- + * ISI + * -------------------------------------------------------------------- */ +static struct resource atmel_isi0_resource[] = { + PBMEM(0xfff02c00), + IRQ(30), +}; +DEFINE_DEV(atmel_isi, 0); +DEV_CLK(hclk, atmel_isi0, hsb, 5); +DEV_CLK(pclk, atmel_isi0, pbb, 11); + +struct platform_device *__init +at32_add_device_isi(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &atmel_isi0_device; + select_peripheral(PB(0), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PB(1), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PB(2), PERIPH_A, 0); /* DATA2 */ + select_peripheral(PB(3), PERIPH_A, 0); /* DATA3 */ + select_peripheral(PB(4), PERIPH_A, 0); /* DATA4 */ + select_peripheral(PB(5), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PB(6), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PB(7), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PB(11), PERIPH_B, 0); /* DATA8 */ + select_peripheral(PB(12), PERIPH_B, 0); /* DATA9 */ + select_peripheral(PB(13), PERIPH_B, 0); /* DATA10 */ + select_peripheral(PB(14), PERIPH_B, 0); /* DATA11 */ + select_peripheral(PB(8), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PB(9), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PB(10), PERIPH_A, 0); /* PCLK */ + break; + + default: + return NULL; + } + + platform_device_register(pdev); + + return pdev; +} + +/* -------------------------------------------------------------------- * GCLK * -------------------------------------------------------------------- */ static struct clk gclk0 = { @@ -1493,6 +1538,8 @@ struct clk *at32_clock_list[] = { &gclk2, &gclk3, &gclk4, + &atmel_isi0_hclk, + &atmel_isi0_pclk, }; unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index 9b36eb8..931f5af 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -55,6 +55,7 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, struct platform_device *at32_add_device_ac97c(unsigned int id); struct platform_device *at32_add_device_abdac(unsigned int id); +struct platform_device *at32_add_device_isi(unsigned int id); /* depending on what's hooked up, not all SSC pins will be used */ #define ATMEL_SSC_TK 0x01 -- 1.5.2.3 >From 6bac229e6999ce8e761baf97975fc5db774721fa Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 21 Feb 2007 15:35:44 +0100 Subject: [PATCH] NGW100: Wire up the ISI Since the NGW100 doesn't actually have a camera on board, this patch merely serves as an example on how you might wire up the ISI on a board that does have a camera. --- arch/avr32/boards/atngw100/setup.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c index d649974..6ca98bb 100644 --- a/arch/avr32/boards/atngw100/setup.c +++ b/arch/avr32/boards/atngw100/setup.c @@ -178,6 +178,11 @@ static int __init atngw100_init(void) at32_add_device_twi(0); #endif + at32_add_device_isi(0); + + /* Master clock for the camera (GCLK0) */ + at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + return 0; } postcore_initcall(atngw100_init); -- 1.5.2.3