1*c1d82b89SHans Verkuil /* 2*c1d82b89SHans Verkuil * Copyright (c) 2011 Atmel Corporation 3*c1d82b89SHans Verkuil * Josh Wu, <josh.wu@atmel.com> 4*c1d82b89SHans Verkuil * 5*c1d82b89SHans Verkuil * Based on previous work by Lars Haring, <lars.haring@atmel.com> 6*c1d82b89SHans Verkuil * and Sedji Gaouaou 7*c1d82b89SHans Verkuil * Based on the bttv driver for Bt848 with respective copyright holders 8*c1d82b89SHans Verkuil * 9*c1d82b89SHans Verkuil * This program is free software; you can redistribute it and/or modify 10*c1d82b89SHans Verkuil * it under the terms of the GNU General Public License version 2 as 11*c1d82b89SHans Verkuil * published by the Free Software Foundation. 12*c1d82b89SHans Verkuil */ 13*c1d82b89SHans Verkuil 14*c1d82b89SHans Verkuil #include <linux/clk.h> 15*c1d82b89SHans Verkuil #include <linux/completion.h> 16*c1d82b89SHans Verkuil #include <linux/delay.h> 17*c1d82b89SHans Verkuil #include <linux/fs.h> 18*c1d82b89SHans Verkuil #include <linux/init.h> 19*c1d82b89SHans Verkuil #include <linux/interrupt.h> 20*c1d82b89SHans Verkuil #include <linux/kernel.h> 21*c1d82b89SHans Verkuil #include <linux/module.h> 22*c1d82b89SHans Verkuil #include <linux/platform_device.h> 23*c1d82b89SHans Verkuil #include <linux/pm_runtime.h> 24*c1d82b89SHans Verkuil #include <linux/slab.h> 25*c1d82b89SHans Verkuil #include <linux/of.h> 26*c1d82b89SHans Verkuil 27*c1d82b89SHans Verkuil #include <linux/videodev2.h> 28*c1d82b89SHans Verkuil #include <media/v4l2-ctrls.h> 29*c1d82b89SHans Verkuil #include <media/v4l2-device.h> 30*c1d82b89SHans Verkuil #include <media/v4l2-dev.h> 31*c1d82b89SHans Verkuil #include <media/v4l2-ioctl.h> 32*c1d82b89SHans Verkuil #include <media/v4l2-event.h> 33*c1d82b89SHans Verkuil #include <media/v4l2-of.h> 34*c1d82b89SHans Verkuil #include <media/videobuf2-dma-contig.h> 35*c1d82b89SHans Verkuil #include <media/v4l2-image-sizes.h> 36*c1d82b89SHans Verkuil 37*c1d82b89SHans Verkuil #include "atmel-isi.h" 38*c1d82b89SHans Verkuil 39*c1d82b89SHans Verkuil #define MAX_SUPPORT_WIDTH 2048 40*c1d82b89SHans Verkuil #define MAX_SUPPORT_HEIGHT 2048 41*c1d82b89SHans Verkuil #define MIN_FRAME_RATE 15 42*c1d82b89SHans Verkuil #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) 43*c1d82b89SHans Verkuil 44*c1d82b89SHans Verkuil /* Frame buffer descriptor */ 45*c1d82b89SHans Verkuil struct fbd { 46*c1d82b89SHans Verkuil /* Physical address of the frame buffer */ 47*c1d82b89SHans Verkuil u32 fb_address; 48*c1d82b89SHans Verkuil /* DMA Control Register(only in HISI2) */ 49*c1d82b89SHans Verkuil u32 dma_ctrl; 50*c1d82b89SHans Verkuil /* Physical address of the next fbd */ 51*c1d82b89SHans Verkuil u32 next_fbd_address; 52*c1d82b89SHans Verkuil }; 53*c1d82b89SHans Verkuil 54*c1d82b89SHans Verkuil static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl) 55*c1d82b89SHans Verkuil { 56*c1d82b89SHans Verkuil fb_desc->dma_ctrl = ctrl; 57*c1d82b89SHans Verkuil } 58*c1d82b89SHans Verkuil 59*c1d82b89SHans Verkuil struct isi_dma_desc { 60*c1d82b89SHans Verkuil struct list_head list; 61*c1d82b89SHans Verkuil struct fbd *p_fbd; 62*c1d82b89SHans Verkuil dma_addr_t fbd_phys; 63*c1d82b89SHans Verkuil }; 64*c1d82b89SHans Verkuil 65*c1d82b89SHans Verkuil /* Frame buffer data */ 66*c1d82b89SHans Verkuil struct frame_buffer { 67*c1d82b89SHans Verkuil struct vb2_v4l2_buffer vb; 68*c1d82b89SHans Verkuil struct isi_dma_desc *p_dma_desc; 69*c1d82b89SHans Verkuil struct list_head list; 70*c1d82b89SHans Verkuil }; 71*c1d82b89SHans Verkuil 72*c1d82b89SHans Verkuil struct isi_graph_entity { 73*c1d82b89SHans Verkuil struct device_node *node; 74*c1d82b89SHans Verkuil 75*c1d82b89SHans Verkuil struct v4l2_async_subdev asd; 76*c1d82b89SHans Verkuil struct v4l2_subdev *subdev; 77*c1d82b89SHans Verkuil }; 78*c1d82b89SHans Verkuil 79*c1d82b89SHans Verkuil /* 80*c1d82b89SHans Verkuil * struct isi_format - ISI media bus format information 81*c1d82b89SHans Verkuil * @fourcc: Fourcc code for this format 82*c1d82b89SHans Verkuil * @mbus_code: V4L2 media bus format code. 83*c1d82b89SHans Verkuil * @bpp: Bytes per pixel (when stored in memory) 84*c1d82b89SHans Verkuil * @swap: Byte swap configuration value 85*c1d82b89SHans Verkuil * @support: Indicates format supported by subdev 86*c1d82b89SHans Verkuil * @skip: Skip duplicate format supported by subdev 87*c1d82b89SHans Verkuil */ 88*c1d82b89SHans Verkuil struct isi_format { 89*c1d82b89SHans Verkuil u32 fourcc; 90*c1d82b89SHans Verkuil u32 mbus_code; 91*c1d82b89SHans Verkuil u8 bpp; 92*c1d82b89SHans Verkuil u32 swap; 93*c1d82b89SHans Verkuil }; 94*c1d82b89SHans Verkuil 95*c1d82b89SHans Verkuil 96*c1d82b89SHans Verkuil struct atmel_isi { 97*c1d82b89SHans Verkuil /* Protects the access of variables shared with the ISR */ 98*c1d82b89SHans Verkuil spinlock_t irqlock; 99*c1d82b89SHans Verkuil struct device *dev; 100*c1d82b89SHans Verkuil void __iomem *regs; 101*c1d82b89SHans Verkuil 102*c1d82b89SHans Verkuil int sequence; 103*c1d82b89SHans Verkuil 104*c1d82b89SHans Verkuil /* Allocate descriptors for dma buffer use */ 105*c1d82b89SHans Verkuil struct fbd *p_fb_descriptors; 106*c1d82b89SHans Verkuil dma_addr_t fb_descriptors_phys; 107*c1d82b89SHans Verkuil struct list_head dma_desc_head; 108*c1d82b89SHans Verkuil struct isi_dma_desc dma_desc[VIDEO_MAX_FRAME]; 109*c1d82b89SHans Verkuil bool enable_preview_path; 110*c1d82b89SHans Verkuil 111*c1d82b89SHans Verkuil struct completion complete; 112*c1d82b89SHans Verkuil /* ISI peripherial clock */ 113*c1d82b89SHans Verkuil struct clk *pclk; 114*c1d82b89SHans Verkuil unsigned int irq; 115*c1d82b89SHans Verkuil 116*c1d82b89SHans Verkuil struct isi_platform_data pdata; 117*c1d82b89SHans Verkuil u16 width_flags; /* max 12 bits */ 118*c1d82b89SHans Verkuil 119*c1d82b89SHans Verkuil struct list_head video_buffer_list; 120*c1d82b89SHans Verkuil struct frame_buffer *active; 121*c1d82b89SHans Verkuil 122*c1d82b89SHans Verkuil struct v4l2_device v4l2_dev; 123*c1d82b89SHans Verkuil struct video_device *vdev; 124*c1d82b89SHans Verkuil struct v4l2_async_notifier notifier; 125*c1d82b89SHans Verkuil struct isi_graph_entity entity; 126*c1d82b89SHans Verkuil struct v4l2_format fmt; 127*c1d82b89SHans Verkuil 128*c1d82b89SHans Verkuil const struct isi_format **user_formats; 129*c1d82b89SHans Verkuil unsigned int num_user_formats; 130*c1d82b89SHans Verkuil const struct isi_format *current_fmt; 131*c1d82b89SHans Verkuil 132*c1d82b89SHans Verkuil struct mutex lock; 133*c1d82b89SHans Verkuil struct vb2_queue queue; 134*c1d82b89SHans Verkuil }; 135*c1d82b89SHans Verkuil 136*c1d82b89SHans Verkuil #define notifier_to_isi(n) container_of(n, struct atmel_isi, notifier) 137*c1d82b89SHans Verkuil 138*c1d82b89SHans Verkuil static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val) 139*c1d82b89SHans Verkuil { 140*c1d82b89SHans Verkuil writel(val, isi->regs + reg); 141*c1d82b89SHans Verkuil } 142*c1d82b89SHans Verkuil static u32 isi_readl(struct atmel_isi *isi, u32 reg) 143*c1d82b89SHans Verkuil { 144*c1d82b89SHans Verkuil return readl(isi->regs + reg); 145*c1d82b89SHans Verkuil } 146*c1d82b89SHans Verkuil 147*c1d82b89SHans Verkuil static void configure_geometry(struct atmel_isi *isi) 148*c1d82b89SHans Verkuil { 149*c1d82b89SHans Verkuil u32 cfg2, psize; 150*c1d82b89SHans Verkuil u32 fourcc = isi->current_fmt->fourcc; 151*c1d82b89SHans Verkuil 152*c1d82b89SHans Verkuil isi->enable_preview_path = fourcc == V4L2_PIX_FMT_RGB565 || 153*c1d82b89SHans Verkuil fourcc == V4L2_PIX_FMT_RGB32; 154*c1d82b89SHans Verkuil 155*c1d82b89SHans Verkuil /* According to sensor's output format to set cfg2 */ 156*c1d82b89SHans Verkuil cfg2 = isi->current_fmt->swap; 157*c1d82b89SHans Verkuil 158*c1d82b89SHans Verkuil isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); 159*c1d82b89SHans Verkuil /* Set width */ 160*c1d82b89SHans Verkuil cfg2 |= ((isi->fmt.fmt.pix.width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & 161*c1d82b89SHans Verkuil ISI_CFG2_IM_HSIZE_MASK; 162*c1d82b89SHans Verkuil /* Set height */ 163*c1d82b89SHans Verkuil cfg2 |= ((isi->fmt.fmt.pix.height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) 164*c1d82b89SHans Verkuil & ISI_CFG2_IM_VSIZE_MASK; 165*c1d82b89SHans Verkuil isi_writel(isi, ISI_CFG2, cfg2); 166*c1d82b89SHans Verkuil 167*c1d82b89SHans Verkuil /* No down sampling, preview size equal to sensor output size */ 168*c1d82b89SHans Verkuil psize = ((isi->fmt.fmt.pix.width - 1) << ISI_PSIZE_PREV_HSIZE_OFFSET) & 169*c1d82b89SHans Verkuil ISI_PSIZE_PREV_HSIZE_MASK; 170*c1d82b89SHans Verkuil psize |= ((isi->fmt.fmt.pix.height - 1) << ISI_PSIZE_PREV_VSIZE_OFFSET) & 171*c1d82b89SHans Verkuil ISI_PSIZE_PREV_VSIZE_MASK; 172*c1d82b89SHans Verkuil isi_writel(isi, ISI_PSIZE, psize); 173*c1d82b89SHans Verkuil isi_writel(isi, ISI_PDECF, ISI_PDECF_NO_SAMPLING); 174*c1d82b89SHans Verkuil } 175*c1d82b89SHans Verkuil 176*c1d82b89SHans Verkuil static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) 177*c1d82b89SHans Verkuil { 178*c1d82b89SHans Verkuil if (isi->active) { 179*c1d82b89SHans Verkuil struct vb2_v4l2_buffer *vbuf = &isi->active->vb; 180*c1d82b89SHans Verkuil struct frame_buffer *buf = isi->active; 181*c1d82b89SHans Verkuil 182*c1d82b89SHans Verkuil list_del_init(&buf->list); 183*c1d82b89SHans Verkuil vbuf->vb2_buf.timestamp = ktime_get_ns(); 184*c1d82b89SHans Verkuil vbuf->sequence = isi->sequence++; 185*c1d82b89SHans Verkuil vbuf->field = V4L2_FIELD_NONE; 186*c1d82b89SHans Verkuil vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); 187*c1d82b89SHans Verkuil } 188*c1d82b89SHans Verkuil 189*c1d82b89SHans Verkuil if (list_empty(&isi->video_buffer_list)) { 190*c1d82b89SHans Verkuil isi->active = NULL; 191*c1d82b89SHans Verkuil } else { 192*c1d82b89SHans Verkuil /* start next dma frame. */ 193*c1d82b89SHans Verkuil isi->active = list_entry(isi->video_buffer_list.next, 194*c1d82b89SHans Verkuil struct frame_buffer, list); 195*c1d82b89SHans Verkuil if (!isi->enable_preview_path) { 196*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_C_DSCR, 197*c1d82b89SHans Verkuil (u32)isi->active->p_dma_desc->fbd_phys); 198*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_C_CTRL, 199*c1d82b89SHans Verkuil ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 200*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); 201*c1d82b89SHans Verkuil } else { 202*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_P_DSCR, 203*c1d82b89SHans Verkuil (u32)isi->active->p_dma_desc->fbd_phys); 204*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_P_CTRL, 205*c1d82b89SHans Verkuil ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 206*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH); 207*c1d82b89SHans Verkuil } 208*c1d82b89SHans Verkuil } 209*c1d82b89SHans Verkuil return IRQ_HANDLED; 210*c1d82b89SHans Verkuil } 211*c1d82b89SHans Verkuil 212*c1d82b89SHans Verkuil /* ISI interrupt service routine */ 213*c1d82b89SHans Verkuil static irqreturn_t isi_interrupt(int irq, void *dev_id) 214*c1d82b89SHans Verkuil { 215*c1d82b89SHans Verkuil struct atmel_isi *isi = dev_id; 216*c1d82b89SHans Verkuil u32 status, mask, pending; 217*c1d82b89SHans Verkuil irqreturn_t ret = IRQ_NONE; 218*c1d82b89SHans Verkuil 219*c1d82b89SHans Verkuil spin_lock(&isi->irqlock); 220*c1d82b89SHans Verkuil 221*c1d82b89SHans Verkuil status = isi_readl(isi, ISI_STATUS); 222*c1d82b89SHans Verkuil mask = isi_readl(isi, ISI_INTMASK); 223*c1d82b89SHans Verkuil pending = status & mask; 224*c1d82b89SHans Verkuil 225*c1d82b89SHans Verkuil if (pending & ISI_CTRL_SRST) { 226*c1d82b89SHans Verkuil complete(&isi->complete); 227*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST); 228*c1d82b89SHans Verkuil ret = IRQ_HANDLED; 229*c1d82b89SHans Verkuil } else if (pending & ISI_CTRL_DIS) { 230*c1d82b89SHans Verkuil complete(&isi->complete); 231*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS); 232*c1d82b89SHans Verkuil ret = IRQ_HANDLED; 233*c1d82b89SHans Verkuil } else { 234*c1d82b89SHans Verkuil if (likely(pending & ISI_SR_CXFR_DONE) || 235*c1d82b89SHans Verkuil likely(pending & ISI_SR_PXFR_DONE)) 236*c1d82b89SHans Verkuil ret = atmel_isi_handle_streaming(isi); 237*c1d82b89SHans Verkuil } 238*c1d82b89SHans Verkuil 239*c1d82b89SHans Verkuil spin_unlock(&isi->irqlock); 240*c1d82b89SHans Verkuil return ret; 241*c1d82b89SHans Verkuil } 242*c1d82b89SHans Verkuil 243*c1d82b89SHans Verkuil #define WAIT_ISI_RESET 1 244*c1d82b89SHans Verkuil #define WAIT_ISI_DISABLE 0 245*c1d82b89SHans Verkuil static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) 246*c1d82b89SHans Verkuil { 247*c1d82b89SHans Verkuil unsigned long timeout; 248*c1d82b89SHans Verkuil /* 249*c1d82b89SHans Verkuil * The reset or disable will only succeed if we have a 250*c1d82b89SHans Verkuil * pixel clock from the camera. 251*c1d82b89SHans Verkuil */ 252*c1d82b89SHans Verkuil init_completion(&isi->complete); 253*c1d82b89SHans Verkuil 254*c1d82b89SHans Verkuil if (wait_reset) { 255*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST); 256*c1d82b89SHans Verkuil isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST); 257*c1d82b89SHans Verkuil } else { 258*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS); 259*c1d82b89SHans Verkuil isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); 260*c1d82b89SHans Verkuil } 261*c1d82b89SHans Verkuil 262*c1d82b89SHans Verkuil timeout = wait_for_completion_timeout(&isi->complete, 263*c1d82b89SHans Verkuil msecs_to_jiffies(500)); 264*c1d82b89SHans Verkuil if (timeout == 0) 265*c1d82b89SHans Verkuil return -ETIMEDOUT; 266*c1d82b89SHans Verkuil 267*c1d82b89SHans Verkuil return 0; 268*c1d82b89SHans Verkuil } 269*c1d82b89SHans Verkuil 270*c1d82b89SHans Verkuil /* ------------------------------------------------------------------ 271*c1d82b89SHans Verkuil Videobuf operations 272*c1d82b89SHans Verkuil ------------------------------------------------------------------*/ 273*c1d82b89SHans Verkuil static int queue_setup(struct vb2_queue *vq, 274*c1d82b89SHans Verkuil unsigned int *nbuffers, unsigned int *nplanes, 275*c1d82b89SHans Verkuil unsigned int sizes[], struct device *alloc_devs[]) 276*c1d82b89SHans Verkuil { 277*c1d82b89SHans Verkuil struct atmel_isi *isi = vb2_get_drv_priv(vq); 278*c1d82b89SHans Verkuil unsigned long size; 279*c1d82b89SHans Verkuil 280*c1d82b89SHans Verkuil size = isi->fmt.fmt.pix.sizeimage; 281*c1d82b89SHans Verkuil 282*c1d82b89SHans Verkuil /* Make sure the image size is large enough. */ 283*c1d82b89SHans Verkuil if (*nplanes) 284*c1d82b89SHans Verkuil return sizes[0] < size ? -EINVAL : 0; 285*c1d82b89SHans Verkuil 286*c1d82b89SHans Verkuil *nplanes = 1; 287*c1d82b89SHans Verkuil sizes[0] = size; 288*c1d82b89SHans Verkuil 289*c1d82b89SHans Verkuil isi->active = NULL; 290*c1d82b89SHans Verkuil 291*c1d82b89SHans Verkuil dev_dbg(isi->dev, "%s, count=%d, size=%ld\n", __func__, 292*c1d82b89SHans Verkuil *nbuffers, size); 293*c1d82b89SHans Verkuil 294*c1d82b89SHans Verkuil return 0; 295*c1d82b89SHans Verkuil } 296*c1d82b89SHans Verkuil 297*c1d82b89SHans Verkuil static int buffer_init(struct vb2_buffer *vb) 298*c1d82b89SHans Verkuil { 299*c1d82b89SHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 300*c1d82b89SHans Verkuil struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 301*c1d82b89SHans Verkuil 302*c1d82b89SHans Verkuil buf->p_dma_desc = NULL; 303*c1d82b89SHans Verkuil INIT_LIST_HEAD(&buf->list); 304*c1d82b89SHans Verkuil 305*c1d82b89SHans Verkuil return 0; 306*c1d82b89SHans Verkuil } 307*c1d82b89SHans Verkuil 308*c1d82b89SHans Verkuil static int buffer_prepare(struct vb2_buffer *vb) 309*c1d82b89SHans Verkuil { 310*c1d82b89SHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 311*c1d82b89SHans Verkuil struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 312*c1d82b89SHans Verkuil struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); 313*c1d82b89SHans Verkuil unsigned long size; 314*c1d82b89SHans Verkuil struct isi_dma_desc *desc; 315*c1d82b89SHans Verkuil 316*c1d82b89SHans Verkuil size = isi->fmt.fmt.pix.sizeimage; 317*c1d82b89SHans Verkuil 318*c1d82b89SHans Verkuil if (vb2_plane_size(vb, 0) < size) { 319*c1d82b89SHans Verkuil dev_err(isi->dev, "%s data will not fit into plane (%lu < %lu)\n", 320*c1d82b89SHans Verkuil __func__, vb2_plane_size(vb, 0), size); 321*c1d82b89SHans Verkuil return -EINVAL; 322*c1d82b89SHans Verkuil } 323*c1d82b89SHans Verkuil 324*c1d82b89SHans Verkuil vb2_set_plane_payload(vb, 0, size); 325*c1d82b89SHans Verkuil 326*c1d82b89SHans Verkuil if (!buf->p_dma_desc) { 327*c1d82b89SHans Verkuil if (list_empty(&isi->dma_desc_head)) { 328*c1d82b89SHans Verkuil dev_err(isi->dev, "Not enough dma descriptors.\n"); 329*c1d82b89SHans Verkuil return -EINVAL; 330*c1d82b89SHans Verkuil } else { 331*c1d82b89SHans Verkuil /* Get an available descriptor */ 332*c1d82b89SHans Verkuil desc = list_entry(isi->dma_desc_head.next, 333*c1d82b89SHans Verkuil struct isi_dma_desc, list); 334*c1d82b89SHans Verkuil /* Delete the descriptor since now it is used */ 335*c1d82b89SHans Verkuil list_del_init(&desc->list); 336*c1d82b89SHans Verkuil 337*c1d82b89SHans Verkuil /* Initialize the dma descriptor */ 338*c1d82b89SHans Verkuil desc->p_fbd->fb_address = 339*c1d82b89SHans Verkuil vb2_dma_contig_plane_dma_addr(vb, 0); 340*c1d82b89SHans Verkuil desc->p_fbd->next_fbd_address = 0; 341*c1d82b89SHans Verkuil set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB); 342*c1d82b89SHans Verkuil 343*c1d82b89SHans Verkuil buf->p_dma_desc = desc; 344*c1d82b89SHans Verkuil } 345*c1d82b89SHans Verkuil } 346*c1d82b89SHans Verkuil return 0; 347*c1d82b89SHans Verkuil } 348*c1d82b89SHans Verkuil 349*c1d82b89SHans Verkuil static void buffer_cleanup(struct vb2_buffer *vb) 350*c1d82b89SHans Verkuil { 351*c1d82b89SHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 352*c1d82b89SHans Verkuil struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); 353*c1d82b89SHans Verkuil struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 354*c1d82b89SHans Verkuil 355*c1d82b89SHans Verkuil /* This descriptor is available now and we add to head list */ 356*c1d82b89SHans Verkuil if (buf->p_dma_desc) 357*c1d82b89SHans Verkuil list_add(&buf->p_dma_desc->list, &isi->dma_desc_head); 358*c1d82b89SHans Verkuil } 359*c1d82b89SHans Verkuil 360*c1d82b89SHans Verkuil static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) 361*c1d82b89SHans Verkuil { 362*c1d82b89SHans Verkuil u32 ctrl, cfg1; 363*c1d82b89SHans Verkuil 364*c1d82b89SHans Verkuil cfg1 = isi_readl(isi, ISI_CFG1); 365*c1d82b89SHans Verkuil /* Enable irq: cxfr for the codec path, pxfr for the preview path */ 366*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTEN, 367*c1d82b89SHans Verkuil ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); 368*c1d82b89SHans Verkuil 369*c1d82b89SHans Verkuil /* Check if already in a frame */ 370*c1d82b89SHans Verkuil if (!isi->enable_preview_path) { 371*c1d82b89SHans Verkuil if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { 372*c1d82b89SHans Verkuil dev_err(isi->dev, "Already in frame handling.\n"); 373*c1d82b89SHans Verkuil return; 374*c1d82b89SHans Verkuil } 375*c1d82b89SHans Verkuil 376*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_C_DSCR, 377*c1d82b89SHans Verkuil (u32)buffer->p_dma_desc->fbd_phys); 378*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_C_CTRL, 379*c1d82b89SHans Verkuil ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 380*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); 381*c1d82b89SHans Verkuil } else { 382*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_P_DSCR, 383*c1d82b89SHans Verkuil (u32)buffer->p_dma_desc->fbd_phys); 384*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_P_CTRL, 385*c1d82b89SHans Verkuil ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 386*c1d82b89SHans Verkuil isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH); 387*c1d82b89SHans Verkuil } 388*c1d82b89SHans Verkuil 389*c1d82b89SHans Verkuil cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK; 390*c1d82b89SHans Verkuil /* Enable linked list */ 391*c1d82b89SHans Verkuil cfg1 |= isi->pdata.frate | ISI_CFG1_DISCR; 392*c1d82b89SHans Verkuil 393*c1d82b89SHans Verkuil /* Enable ISI */ 394*c1d82b89SHans Verkuil ctrl = ISI_CTRL_EN; 395*c1d82b89SHans Verkuil 396*c1d82b89SHans Verkuil if (!isi->enable_preview_path) 397*c1d82b89SHans Verkuil ctrl |= ISI_CTRL_CDC; 398*c1d82b89SHans Verkuil 399*c1d82b89SHans Verkuil isi_writel(isi, ISI_CTRL, ctrl); 400*c1d82b89SHans Verkuil isi_writel(isi, ISI_CFG1, cfg1); 401*c1d82b89SHans Verkuil } 402*c1d82b89SHans Verkuil 403*c1d82b89SHans Verkuil static void buffer_queue(struct vb2_buffer *vb) 404*c1d82b89SHans Verkuil { 405*c1d82b89SHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 406*c1d82b89SHans Verkuil struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); 407*c1d82b89SHans Verkuil struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 408*c1d82b89SHans Verkuil unsigned long flags = 0; 409*c1d82b89SHans Verkuil 410*c1d82b89SHans Verkuil spin_lock_irqsave(&isi->irqlock, flags); 411*c1d82b89SHans Verkuil list_add_tail(&buf->list, &isi->video_buffer_list); 412*c1d82b89SHans Verkuil 413*c1d82b89SHans Verkuil if (isi->active == NULL) { 414*c1d82b89SHans Verkuil isi->active = buf; 415*c1d82b89SHans Verkuil if (vb2_is_streaming(vb->vb2_queue)) 416*c1d82b89SHans Verkuil start_dma(isi, buf); 417*c1d82b89SHans Verkuil } 418*c1d82b89SHans Verkuil spin_unlock_irqrestore(&isi->irqlock, flags); 419*c1d82b89SHans Verkuil } 420*c1d82b89SHans Verkuil 421*c1d82b89SHans Verkuil static int start_streaming(struct vb2_queue *vq, unsigned int count) 422*c1d82b89SHans Verkuil { 423*c1d82b89SHans Verkuil struct atmel_isi *isi = vb2_get_drv_priv(vq); 424*c1d82b89SHans Verkuil struct frame_buffer *buf, *node; 425*c1d82b89SHans Verkuil int ret; 426*c1d82b89SHans Verkuil 427*c1d82b89SHans Verkuil /* Enable stream on the sub device */ 428*c1d82b89SHans Verkuil ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 1); 429*c1d82b89SHans Verkuil if (ret && ret != -ENOIOCTLCMD) { 430*c1d82b89SHans Verkuil dev_err(isi->dev, "stream on failed in subdev\n"); 431*c1d82b89SHans Verkuil goto err_start_stream; 432*c1d82b89SHans Verkuil } 433*c1d82b89SHans Verkuil 434*c1d82b89SHans Verkuil pm_runtime_get_sync(isi->dev); 435*c1d82b89SHans Verkuil 436*c1d82b89SHans Verkuil /* Reset ISI */ 437*c1d82b89SHans Verkuil ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); 438*c1d82b89SHans Verkuil if (ret < 0) { 439*c1d82b89SHans Verkuil dev_err(isi->dev, "Reset ISI timed out\n"); 440*c1d82b89SHans Verkuil goto err_reset; 441*c1d82b89SHans Verkuil } 442*c1d82b89SHans Verkuil /* Disable all interrupts */ 443*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTDIS, (u32)~0UL); 444*c1d82b89SHans Verkuil 445*c1d82b89SHans Verkuil isi->sequence = 0; 446*c1d82b89SHans Verkuil configure_geometry(isi); 447*c1d82b89SHans Verkuil 448*c1d82b89SHans Verkuil spin_lock_irq(&isi->irqlock); 449*c1d82b89SHans Verkuil /* Clear any pending interrupt */ 450*c1d82b89SHans Verkuil isi_readl(isi, ISI_STATUS); 451*c1d82b89SHans Verkuil 452*c1d82b89SHans Verkuil start_dma(isi, isi->active); 453*c1d82b89SHans Verkuil spin_unlock_irq(&isi->irqlock); 454*c1d82b89SHans Verkuil 455*c1d82b89SHans Verkuil return 0; 456*c1d82b89SHans Verkuil 457*c1d82b89SHans Verkuil err_reset: 458*c1d82b89SHans Verkuil pm_runtime_put(isi->dev); 459*c1d82b89SHans Verkuil v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); 460*c1d82b89SHans Verkuil 461*c1d82b89SHans Verkuil err_start_stream: 462*c1d82b89SHans Verkuil spin_lock_irq(&isi->irqlock); 463*c1d82b89SHans Verkuil isi->active = NULL; 464*c1d82b89SHans Verkuil /* Release all active buffers */ 465*c1d82b89SHans Verkuil list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { 466*c1d82b89SHans Verkuil list_del_init(&buf->list); 467*c1d82b89SHans Verkuil vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 468*c1d82b89SHans Verkuil } 469*c1d82b89SHans Verkuil spin_unlock_irq(&isi->irqlock); 470*c1d82b89SHans Verkuil 471*c1d82b89SHans Verkuil return ret; 472*c1d82b89SHans Verkuil } 473*c1d82b89SHans Verkuil 474*c1d82b89SHans Verkuil /* abort streaming and wait for last buffer */ 475*c1d82b89SHans Verkuil static void stop_streaming(struct vb2_queue *vq) 476*c1d82b89SHans Verkuil { 477*c1d82b89SHans Verkuil struct atmel_isi *isi = vb2_get_drv_priv(vq); 478*c1d82b89SHans Verkuil struct frame_buffer *buf, *node; 479*c1d82b89SHans Verkuil int ret = 0; 480*c1d82b89SHans Verkuil unsigned long timeout; 481*c1d82b89SHans Verkuil 482*c1d82b89SHans Verkuil /* Disable stream on the sub device */ 483*c1d82b89SHans Verkuil ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); 484*c1d82b89SHans Verkuil if (ret && ret != -ENOIOCTLCMD) 485*c1d82b89SHans Verkuil dev_err(isi->dev, "stream off failed in subdev\n"); 486*c1d82b89SHans Verkuil 487*c1d82b89SHans Verkuil spin_lock_irq(&isi->irqlock); 488*c1d82b89SHans Verkuil isi->active = NULL; 489*c1d82b89SHans Verkuil /* Release all active buffers */ 490*c1d82b89SHans Verkuil list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { 491*c1d82b89SHans Verkuil list_del_init(&buf->list); 492*c1d82b89SHans Verkuil vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 493*c1d82b89SHans Verkuil } 494*c1d82b89SHans Verkuil spin_unlock_irq(&isi->irqlock); 495*c1d82b89SHans Verkuil 496*c1d82b89SHans Verkuil if (!isi->enable_preview_path) { 497*c1d82b89SHans Verkuil timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ; 498*c1d82b89SHans Verkuil /* Wait until the end of the current frame. */ 499*c1d82b89SHans Verkuil while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) && 500*c1d82b89SHans Verkuil time_before(jiffies, timeout)) 501*c1d82b89SHans Verkuil msleep(1); 502*c1d82b89SHans Verkuil 503*c1d82b89SHans Verkuil if (time_after(jiffies, timeout)) 504*c1d82b89SHans Verkuil dev_err(isi->dev, 505*c1d82b89SHans Verkuil "Timeout waiting for finishing codec request\n"); 506*c1d82b89SHans Verkuil } 507*c1d82b89SHans Verkuil 508*c1d82b89SHans Verkuil /* Disable interrupts */ 509*c1d82b89SHans Verkuil isi_writel(isi, ISI_INTDIS, 510*c1d82b89SHans Verkuil ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); 511*c1d82b89SHans Verkuil 512*c1d82b89SHans Verkuil /* Disable ISI and wait for it is done */ 513*c1d82b89SHans Verkuil ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); 514*c1d82b89SHans Verkuil if (ret < 0) 515*c1d82b89SHans Verkuil dev_err(isi->dev, "Disable ISI timed out\n"); 516*c1d82b89SHans Verkuil 517*c1d82b89SHans Verkuil pm_runtime_put(isi->dev); 518*c1d82b89SHans Verkuil } 519*c1d82b89SHans Verkuil 520*c1d82b89SHans Verkuil static const struct vb2_ops isi_video_qops = { 521*c1d82b89SHans Verkuil .queue_setup = queue_setup, 522*c1d82b89SHans Verkuil .buf_init = buffer_init, 523*c1d82b89SHans Verkuil .buf_prepare = buffer_prepare, 524*c1d82b89SHans Verkuil .buf_cleanup = buffer_cleanup, 525*c1d82b89SHans Verkuil .buf_queue = buffer_queue, 526*c1d82b89SHans Verkuil .start_streaming = start_streaming, 527*c1d82b89SHans Verkuil .stop_streaming = stop_streaming, 528*c1d82b89SHans Verkuil .wait_prepare = vb2_ops_wait_prepare, 529*c1d82b89SHans Verkuil .wait_finish = vb2_ops_wait_finish, 530*c1d82b89SHans Verkuil }; 531*c1d82b89SHans Verkuil 532*c1d82b89SHans Verkuil static int isi_g_fmt_vid_cap(struct file *file, void *priv, 533*c1d82b89SHans Verkuil struct v4l2_format *fmt) 534*c1d82b89SHans Verkuil { 535*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 536*c1d82b89SHans Verkuil 537*c1d82b89SHans Verkuil *fmt = isi->fmt; 538*c1d82b89SHans Verkuil 539*c1d82b89SHans Verkuil return 0; 540*c1d82b89SHans Verkuil } 541*c1d82b89SHans Verkuil 542*c1d82b89SHans Verkuil static const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, 543*c1d82b89SHans Verkuil unsigned int fourcc) 544*c1d82b89SHans Verkuil { 545*c1d82b89SHans Verkuil unsigned int num_formats = isi->num_user_formats; 546*c1d82b89SHans Verkuil const struct isi_format *fmt; 547*c1d82b89SHans Verkuil unsigned int i; 548*c1d82b89SHans Verkuil 549*c1d82b89SHans Verkuil for (i = 0; i < num_formats; i++) { 550*c1d82b89SHans Verkuil fmt = isi->user_formats[i]; 551*c1d82b89SHans Verkuil if (fmt->fourcc == fourcc) 552*c1d82b89SHans Verkuil return fmt; 553*c1d82b89SHans Verkuil } 554*c1d82b89SHans Verkuil 555*c1d82b89SHans Verkuil return NULL; 556*c1d82b89SHans Verkuil } 557*c1d82b89SHans Verkuil 558*c1d82b89SHans Verkuil static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, 559*c1d82b89SHans Verkuil const struct isi_format **current_fmt) 560*c1d82b89SHans Verkuil { 561*c1d82b89SHans Verkuil const struct isi_format *isi_fmt; 562*c1d82b89SHans Verkuil struct v4l2_pix_format *pixfmt = &f->fmt.pix; 563*c1d82b89SHans Verkuil struct v4l2_subdev_pad_config pad_cfg; 564*c1d82b89SHans Verkuil struct v4l2_subdev_format format = { 565*c1d82b89SHans Verkuil .which = V4L2_SUBDEV_FORMAT_TRY, 566*c1d82b89SHans Verkuil }; 567*c1d82b89SHans Verkuil int ret; 568*c1d82b89SHans Verkuil 569*c1d82b89SHans Verkuil if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 570*c1d82b89SHans Verkuil return -EINVAL; 571*c1d82b89SHans Verkuil 572*c1d82b89SHans Verkuil isi_fmt = find_format_by_fourcc(isi, pixfmt->pixelformat); 573*c1d82b89SHans Verkuil if (!isi_fmt) { 574*c1d82b89SHans Verkuil isi_fmt = isi->user_formats[isi->num_user_formats - 1]; 575*c1d82b89SHans Verkuil pixfmt->pixelformat = isi_fmt->fourcc; 576*c1d82b89SHans Verkuil } 577*c1d82b89SHans Verkuil 578*c1d82b89SHans Verkuil /* Limit to Atmel ISC hardware capabilities */ 579*c1d82b89SHans Verkuil if (pixfmt->width > MAX_SUPPORT_WIDTH) 580*c1d82b89SHans Verkuil pixfmt->width = MAX_SUPPORT_WIDTH; 581*c1d82b89SHans Verkuil if (pixfmt->height > MAX_SUPPORT_HEIGHT) 582*c1d82b89SHans Verkuil pixfmt->height = MAX_SUPPORT_HEIGHT; 583*c1d82b89SHans Verkuil 584*c1d82b89SHans Verkuil v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code); 585*c1d82b89SHans Verkuil ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, 586*c1d82b89SHans Verkuil &pad_cfg, &format); 587*c1d82b89SHans Verkuil if (ret < 0) 588*c1d82b89SHans Verkuil return ret; 589*c1d82b89SHans Verkuil 590*c1d82b89SHans Verkuil v4l2_fill_pix_format(pixfmt, &format.format); 591*c1d82b89SHans Verkuil 592*c1d82b89SHans Verkuil pixfmt->field = V4L2_FIELD_NONE; 593*c1d82b89SHans Verkuil pixfmt->bytesperline = pixfmt->width * isi_fmt->bpp; 594*c1d82b89SHans Verkuil pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; 595*c1d82b89SHans Verkuil 596*c1d82b89SHans Verkuil if (current_fmt) 597*c1d82b89SHans Verkuil *current_fmt = isi_fmt; 598*c1d82b89SHans Verkuil 599*c1d82b89SHans Verkuil return 0; 600*c1d82b89SHans Verkuil } 601*c1d82b89SHans Verkuil 602*c1d82b89SHans Verkuil static int isi_set_fmt(struct atmel_isi *isi, struct v4l2_format *f) 603*c1d82b89SHans Verkuil { 604*c1d82b89SHans Verkuil struct v4l2_subdev_format format = { 605*c1d82b89SHans Verkuil .which = V4L2_SUBDEV_FORMAT_ACTIVE, 606*c1d82b89SHans Verkuil }; 607*c1d82b89SHans Verkuil const struct isi_format *current_fmt; 608*c1d82b89SHans Verkuil int ret; 609*c1d82b89SHans Verkuil 610*c1d82b89SHans Verkuil ret = isi_try_fmt(isi, f, ¤t_fmt); 611*c1d82b89SHans Verkuil if (ret) 612*c1d82b89SHans Verkuil return ret; 613*c1d82b89SHans Verkuil 614*c1d82b89SHans Verkuil v4l2_fill_mbus_format(&format.format, &f->fmt.pix, 615*c1d82b89SHans Verkuil current_fmt->mbus_code); 616*c1d82b89SHans Verkuil ret = v4l2_subdev_call(isi->entity.subdev, pad, 617*c1d82b89SHans Verkuil set_fmt, NULL, &format); 618*c1d82b89SHans Verkuil if (ret < 0) 619*c1d82b89SHans Verkuil return ret; 620*c1d82b89SHans Verkuil 621*c1d82b89SHans Verkuil isi->fmt = *f; 622*c1d82b89SHans Verkuil isi->current_fmt = current_fmt; 623*c1d82b89SHans Verkuil 624*c1d82b89SHans Verkuil return 0; 625*c1d82b89SHans Verkuil } 626*c1d82b89SHans Verkuil 627*c1d82b89SHans Verkuil static int isi_s_fmt_vid_cap(struct file *file, void *priv, 628*c1d82b89SHans Verkuil struct v4l2_format *f) 629*c1d82b89SHans Verkuil { 630*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 631*c1d82b89SHans Verkuil 632*c1d82b89SHans Verkuil if (vb2_is_streaming(&isi->queue)) 633*c1d82b89SHans Verkuil return -EBUSY; 634*c1d82b89SHans Verkuil 635*c1d82b89SHans Verkuil return isi_set_fmt(isi, f); 636*c1d82b89SHans Verkuil } 637*c1d82b89SHans Verkuil 638*c1d82b89SHans Verkuil static int isi_try_fmt_vid_cap(struct file *file, void *priv, 639*c1d82b89SHans Verkuil struct v4l2_format *f) 640*c1d82b89SHans Verkuil { 641*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 642*c1d82b89SHans Verkuil 643*c1d82b89SHans Verkuil return isi_try_fmt(isi, f, NULL); 644*c1d82b89SHans Verkuil } 645*c1d82b89SHans Verkuil 646*c1d82b89SHans Verkuil static int isi_enum_fmt_vid_cap(struct file *file, void *priv, 647*c1d82b89SHans Verkuil struct v4l2_fmtdesc *f) 648*c1d82b89SHans Verkuil { 649*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 650*c1d82b89SHans Verkuil 651*c1d82b89SHans Verkuil if (f->index >= isi->num_user_formats) 652*c1d82b89SHans Verkuil return -EINVAL; 653*c1d82b89SHans Verkuil 654*c1d82b89SHans Verkuil f->pixelformat = isi->user_formats[f->index]->fourcc; 655*c1d82b89SHans Verkuil return 0; 656*c1d82b89SHans Verkuil } 657*c1d82b89SHans Verkuil 658*c1d82b89SHans Verkuil static int isi_querycap(struct file *file, void *priv, 659*c1d82b89SHans Verkuil struct v4l2_capability *cap) 660*c1d82b89SHans Verkuil { 661*c1d82b89SHans Verkuil strlcpy(cap->driver, "atmel-isi", sizeof(cap->driver)); 662*c1d82b89SHans Verkuil strlcpy(cap->card, "Atmel Image Sensor Interface", sizeof(cap->card)); 663*c1d82b89SHans Verkuil strlcpy(cap->bus_info, "platform:isi", sizeof(cap->bus_info)); 664*c1d82b89SHans Verkuil return 0; 665*c1d82b89SHans Verkuil } 666*c1d82b89SHans Verkuil 667*c1d82b89SHans Verkuil static int isi_enum_input(struct file *file, void *priv, 668*c1d82b89SHans Verkuil struct v4l2_input *i) 669*c1d82b89SHans Verkuil { 670*c1d82b89SHans Verkuil if (i->index != 0) 671*c1d82b89SHans Verkuil return -EINVAL; 672*c1d82b89SHans Verkuil 673*c1d82b89SHans Verkuil i->type = V4L2_INPUT_TYPE_CAMERA; 674*c1d82b89SHans Verkuil strlcpy(i->name, "Camera", sizeof(i->name)); 675*c1d82b89SHans Verkuil return 0; 676*c1d82b89SHans Verkuil } 677*c1d82b89SHans Verkuil 678*c1d82b89SHans Verkuil static int isi_g_input(struct file *file, void *priv, unsigned int *i) 679*c1d82b89SHans Verkuil { 680*c1d82b89SHans Verkuil *i = 0; 681*c1d82b89SHans Verkuil return 0; 682*c1d82b89SHans Verkuil } 683*c1d82b89SHans Verkuil 684*c1d82b89SHans Verkuil static int isi_s_input(struct file *file, void *priv, unsigned int i) 685*c1d82b89SHans Verkuil { 686*c1d82b89SHans Verkuil if (i > 0) 687*c1d82b89SHans Verkuil return -EINVAL; 688*c1d82b89SHans Verkuil return 0; 689*c1d82b89SHans Verkuil } 690*c1d82b89SHans Verkuil 691*c1d82b89SHans Verkuil static int isi_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 692*c1d82b89SHans Verkuil { 693*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 694*c1d82b89SHans Verkuil 695*c1d82b89SHans Verkuil if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 696*c1d82b89SHans Verkuil return -EINVAL; 697*c1d82b89SHans Verkuil 698*c1d82b89SHans Verkuil a->parm.capture.readbuffers = 2; 699*c1d82b89SHans Verkuil return v4l2_subdev_call(isi->entity.subdev, video, g_parm, a); 700*c1d82b89SHans Verkuil } 701*c1d82b89SHans Verkuil 702*c1d82b89SHans Verkuil static int isi_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 703*c1d82b89SHans Verkuil { 704*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 705*c1d82b89SHans Verkuil 706*c1d82b89SHans Verkuil if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 707*c1d82b89SHans Verkuil return -EINVAL; 708*c1d82b89SHans Verkuil 709*c1d82b89SHans Verkuil a->parm.capture.readbuffers = 2; 710*c1d82b89SHans Verkuil return v4l2_subdev_call(isi->entity.subdev, video, s_parm, a); 711*c1d82b89SHans Verkuil } 712*c1d82b89SHans Verkuil 713*c1d82b89SHans Verkuil static int isi_enum_framesizes(struct file *file, void *fh, 714*c1d82b89SHans Verkuil struct v4l2_frmsizeenum *fsize) 715*c1d82b89SHans Verkuil { 716*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 717*c1d82b89SHans Verkuil const struct isi_format *isi_fmt; 718*c1d82b89SHans Verkuil struct v4l2_subdev_frame_size_enum fse = { 719*c1d82b89SHans Verkuil .index = fsize->index, 720*c1d82b89SHans Verkuil .which = V4L2_SUBDEV_FORMAT_ACTIVE, 721*c1d82b89SHans Verkuil }; 722*c1d82b89SHans Verkuil int ret; 723*c1d82b89SHans Verkuil 724*c1d82b89SHans Verkuil isi_fmt = find_format_by_fourcc(isi, fsize->pixel_format); 725*c1d82b89SHans Verkuil if (!isi_fmt) 726*c1d82b89SHans Verkuil return -EINVAL; 727*c1d82b89SHans Verkuil 728*c1d82b89SHans Verkuil fse.code = isi_fmt->mbus_code; 729*c1d82b89SHans Verkuil 730*c1d82b89SHans Verkuil ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, 731*c1d82b89SHans Verkuil NULL, &fse); 732*c1d82b89SHans Verkuil if (ret) 733*c1d82b89SHans Verkuil return ret; 734*c1d82b89SHans Verkuil 735*c1d82b89SHans Verkuil fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 736*c1d82b89SHans Verkuil fsize->discrete.width = fse.max_width; 737*c1d82b89SHans Verkuil fsize->discrete.height = fse.max_height; 738*c1d82b89SHans Verkuil 739*c1d82b89SHans Verkuil return 0; 740*c1d82b89SHans Verkuil } 741*c1d82b89SHans Verkuil 742*c1d82b89SHans Verkuil static int isi_enum_frameintervals(struct file *file, void *fh, 743*c1d82b89SHans Verkuil struct v4l2_frmivalenum *fival) 744*c1d82b89SHans Verkuil { 745*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 746*c1d82b89SHans Verkuil const struct isi_format *isi_fmt; 747*c1d82b89SHans Verkuil struct v4l2_subdev_frame_interval_enum fie = { 748*c1d82b89SHans Verkuil .index = fival->index, 749*c1d82b89SHans Verkuil .width = fival->width, 750*c1d82b89SHans Verkuil .height = fival->height, 751*c1d82b89SHans Verkuil .which = V4L2_SUBDEV_FORMAT_ACTIVE, 752*c1d82b89SHans Verkuil }; 753*c1d82b89SHans Verkuil int ret; 754*c1d82b89SHans Verkuil 755*c1d82b89SHans Verkuil isi_fmt = find_format_by_fourcc(isi, fival->pixel_format); 756*c1d82b89SHans Verkuil if (!isi_fmt) 757*c1d82b89SHans Verkuil return -EINVAL; 758*c1d82b89SHans Verkuil 759*c1d82b89SHans Verkuil fie.code = isi_fmt->mbus_code; 760*c1d82b89SHans Verkuil 761*c1d82b89SHans Verkuil ret = v4l2_subdev_call(isi->entity.subdev, pad, 762*c1d82b89SHans Verkuil enum_frame_interval, NULL, &fie); 763*c1d82b89SHans Verkuil if (ret) 764*c1d82b89SHans Verkuil return ret; 765*c1d82b89SHans Verkuil 766*c1d82b89SHans Verkuil fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 767*c1d82b89SHans Verkuil fival->discrete = fie.interval; 768*c1d82b89SHans Verkuil 769*c1d82b89SHans Verkuil return 0; 770*c1d82b89SHans Verkuil } 771*c1d82b89SHans Verkuil 772*c1d82b89SHans Verkuil static void isi_camera_set_bus_param(struct atmel_isi *isi) 773*c1d82b89SHans Verkuil { 774*c1d82b89SHans Verkuil u32 cfg1 = 0; 775*c1d82b89SHans Verkuil 776*c1d82b89SHans Verkuil /* set bus param for ISI */ 777*c1d82b89SHans Verkuil if (isi->pdata.hsync_act_low) 778*c1d82b89SHans Verkuil cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW; 779*c1d82b89SHans Verkuil if (isi->pdata.vsync_act_low) 780*c1d82b89SHans Verkuil cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW; 781*c1d82b89SHans Verkuil if (isi->pdata.pclk_act_falling) 782*c1d82b89SHans Verkuil cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; 783*c1d82b89SHans Verkuil if (isi->pdata.has_emb_sync) 784*c1d82b89SHans Verkuil cfg1 |= ISI_CFG1_EMB_SYNC; 785*c1d82b89SHans Verkuil if (isi->pdata.full_mode) 786*c1d82b89SHans Verkuil cfg1 |= ISI_CFG1_FULL_MODE; 787*c1d82b89SHans Verkuil 788*c1d82b89SHans Verkuil cfg1 |= ISI_CFG1_THMASK_BEATS_16; 789*c1d82b89SHans Verkuil 790*c1d82b89SHans Verkuil /* Enable PM and peripheral clock before operate isi registers */ 791*c1d82b89SHans Verkuil pm_runtime_get_sync(isi->dev); 792*c1d82b89SHans Verkuil 793*c1d82b89SHans Verkuil isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); 794*c1d82b89SHans Verkuil isi_writel(isi, ISI_CFG1, cfg1); 795*c1d82b89SHans Verkuil 796*c1d82b89SHans Verkuil pm_runtime_put(isi->dev); 797*c1d82b89SHans Verkuil } 798*c1d82b89SHans Verkuil 799*c1d82b89SHans Verkuil /* -----------------------------------------------------------------------*/ 800*c1d82b89SHans Verkuil static int atmel_isi_parse_dt(struct atmel_isi *isi, 801*c1d82b89SHans Verkuil struct platform_device *pdev) 802*c1d82b89SHans Verkuil { 803*c1d82b89SHans Verkuil struct device_node *np = pdev->dev.of_node; 804*c1d82b89SHans Verkuil struct v4l2_of_endpoint ep; 805*c1d82b89SHans Verkuil int err; 806*c1d82b89SHans Verkuil 807*c1d82b89SHans Verkuil /* Default settings for ISI */ 808*c1d82b89SHans Verkuil isi->pdata.full_mode = 1; 809*c1d82b89SHans Verkuil isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; 810*c1d82b89SHans Verkuil 811*c1d82b89SHans Verkuil np = of_graph_get_next_endpoint(np, NULL); 812*c1d82b89SHans Verkuil if (!np) { 813*c1d82b89SHans Verkuil dev_err(&pdev->dev, "Could not find the endpoint\n"); 814*c1d82b89SHans Verkuil return -EINVAL; 815*c1d82b89SHans Verkuil } 816*c1d82b89SHans Verkuil 817*c1d82b89SHans Verkuil err = v4l2_of_parse_endpoint(np, &ep); 818*c1d82b89SHans Verkuil of_node_put(np); 819*c1d82b89SHans Verkuil if (err) { 820*c1d82b89SHans Verkuil dev_err(&pdev->dev, "Could not parse the endpoint\n"); 821*c1d82b89SHans Verkuil return err; 822*c1d82b89SHans Verkuil } 823*c1d82b89SHans Verkuil 824*c1d82b89SHans Verkuil switch (ep.bus.parallel.bus_width) { 825*c1d82b89SHans Verkuil case 8: 826*c1d82b89SHans Verkuil isi->pdata.data_width_flags = ISI_DATAWIDTH_8; 827*c1d82b89SHans Verkuil break; 828*c1d82b89SHans Verkuil case 10: 829*c1d82b89SHans Verkuil isi->pdata.data_width_flags = 830*c1d82b89SHans Verkuil ISI_DATAWIDTH_8 | ISI_DATAWIDTH_10; 831*c1d82b89SHans Verkuil break; 832*c1d82b89SHans Verkuil default: 833*c1d82b89SHans Verkuil dev_err(&pdev->dev, "Unsupported bus width: %d\n", 834*c1d82b89SHans Verkuil ep.bus.parallel.bus_width); 835*c1d82b89SHans Verkuil return -EINVAL; 836*c1d82b89SHans Verkuil } 837*c1d82b89SHans Verkuil 838*c1d82b89SHans Verkuil if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) 839*c1d82b89SHans Verkuil isi->pdata.hsync_act_low = true; 840*c1d82b89SHans Verkuil if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) 841*c1d82b89SHans Verkuil isi->pdata.vsync_act_low = true; 842*c1d82b89SHans Verkuil if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) 843*c1d82b89SHans Verkuil isi->pdata.pclk_act_falling = true; 844*c1d82b89SHans Verkuil 845*c1d82b89SHans Verkuil if (ep.bus_type == V4L2_MBUS_BT656) 846*c1d82b89SHans Verkuil isi->pdata.has_emb_sync = true; 847*c1d82b89SHans Verkuil 848*c1d82b89SHans Verkuil return 0; 849*c1d82b89SHans Verkuil } 850*c1d82b89SHans Verkuil 851*c1d82b89SHans Verkuil static int isi_open(struct file *file) 852*c1d82b89SHans Verkuil { 853*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 854*c1d82b89SHans Verkuil struct v4l2_subdev *sd = isi->entity.subdev; 855*c1d82b89SHans Verkuil int ret; 856*c1d82b89SHans Verkuil 857*c1d82b89SHans Verkuil if (mutex_lock_interruptible(&isi->lock)) 858*c1d82b89SHans Verkuil return -ERESTARTSYS; 859*c1d82b89SHans Verkuil 860*c1d82b89SHans Verkuil ret = v4l2_fh_open(file); 861*c1d82b89SHans Verkuil if (ret < 0) 862*c1d82b89SHans Verkuil goto unlock; 863*c1d82b89SHans Verkuil 864*c1d82b89SHans Verkuil if (!v4l2_fh_is_singular_file(file)) 865*c1d82b89SHans Verkuil goto fh_rel; 866*c1d82b89SHans Verkuil 867*c1d82b89SHans Verkuil ret = v4l2_subdev_call(sd, core, s_power, 1); 868*c1d82b89SHans Verkuil if (ret < 0 && ret != -ENOIOCTLCMD) 869*c1d82b89SHans Verkuil goto fh_rel; 870*c1d82b89SHans Verkuil 871*c1d82b89SHans Verkuil ret = isi_set_fmt(isi, &isi->fmt); 872*c1d82b89SHans Verkuil if (ret) 873*c1d82b89SHans Verkuil v4l2_subdev_call(sd, core, s_power, 0); 874*c1d82b89SHans Verkuil fh_rel: 875*c1d82b89SHans Verkuil if (ret) 876*c1d82b89SHans Verkuil v4l2_fh_release(file); 877*c1d82b89SHans Verkuil unlock: 878*c1d82b89SHans Verkuil mutex_unlock(&isi->lock); 879*c1d82b89SHans Verkuil return ret; 880*c1d82b89SHans Verkuil } 881*c1d82b89SHans Verkuil 882*c1d82b89SHans Verkuil static int isi_release(struct file *file) 883*c1d82b89SHans Verkuil { 884*c1d82b89SHans Verkuil struct atmel_isi *isi = video_drvdata(file); 885*c1d82b89SHans Verkuil struct v4l2_subdev *sd = isi->entity.subdev; 886*c1d82b89SHans Verkuil bool fh_singular; 887*c1d82b89SHans Verkuil int ret; 888*c1d82b89SHans Verkuil 889*c1d82b89SHans Verkuil mutex_lock(&isi->lock); 890*c1d82b89SHans Verkuil 891*c1d82b89SHans Verkuil fh_singular = v4l2_fh_is_singular_file(file); 892*c1d82b89SHans Verkuil 893*c1d82b89SHans Verkuil ret = _vb2_fop_release(file, NULL); 894*c1d82b89SHans Verkuil 895*c1d82b89SHans Verkuil if (fh_singular) 896*c1d82b89SHans Verkuil v4l2_subdev_call(sd, core, s_power, 0); 897*c1d82b89SHans Verkuil 898*c1d82b89SHans Verkuil mutex_unlock(&isi->lock); 899*c1d82b89SHans Verkuil 900*c1d82b89SHans Verkuil return ret; 901*c1d82b89SHans Verkuil } 902*c1d82b89SHans Verkuil 903*c1d82b89SHans Verkuil static const struct v4l2_ioctl_ops isi_ioctl_ops = { 904*c1d82b89SHans Verkuil .vidioc_querycap = isi_querycap, 905*c1d82b89SHans Verkuil 906*c1d82b89SHans Verkuil .vidioc_try_fmt_vid_cap = isi_try_fmt_vid_cap, 907*c1d82b89SHans Verkuil .vidioc_g_fmt_vid_cap = isi_g_fmt_vid_cap, 908*c1d82b89SHans Verkuil .vidioc_s_fmt_vid_cap = isi_s_fmt_vid_cap, 909*c1d82b89SHans Verkuil .vidioc_enum_fmt_vid_cap = isi_enum_fmt_vid_cap, 910*c1d82b89SHans Verkuil 911*c1d82b89SHans Verkuil .vidioc_enum_input = isi_enum_input, 912*c1d82b89SHans Verkuil .vidioc_g_input = isi_g_input, 913*c1d82b89SHans Verkuil .vidioc_s_input = isi_s_input, 914*c1d82b89SHans Verkuil 915*c1d82b89SHans Verkuil .vidioc_g_parm = isi_g_parm, 916*c1d82b89SHans Verkuil .vidioc_s_parm = isi_s_parm, 917*c1d82b89SHans Verkuil .vidioc_enum_framesizes = isi_enum_framesizes, 918*c1d82b89SHans Verkuil .vidioc_enum_frameintervals = isi_enum_frameintervals, 919*c1d82b89SHans Verkuil 920*c1d82b89SHans Verkuil .vidioc_reqbufs = vb2_ioctl_reqbufs, 921*c1d82b89SHans Verkuil .vidioc_create_bufs = vb2_ioctl_create_bufs, 922*c1d82b89SHans Verkuil .vidioc_querybuf = vb2_ioctl_querybuf, 923*c1d82b89SHans Verkuil .vidioc_qbuf = vb2_ioctl_qbuf, 924*c1d82b89SHans Verkuil .vidioc_dqbuf = vb2_ioctl_dqbuf, 925*c1d82b89SHans Verkuil .vidioc_expbuf = vb2_ioctl_expbuf, 926*c1d82b89SHans Verkuil .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 927*c1d82b89SHans Verkuil .vidioc_streamon = vb2_ioctl_streamon, 928*c1d82b89SHans Verkuil .vidioc_streamoff = vb2_ioctl_streamoff, 929*c1d82b89SHans Verkuil 930*c1d82b89SHans Verkuil .vidioc_log_status = v4l2_ctrl_log_status, 931*c1d82b89SHans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 932*c1d82b89SHans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 933*c1d82b89SHans Verkuil }; 934*c1d82b89SHans Verkuil 935*c1d82b89SHans Verkuil static const struct v4l2_file_operations isi_fops = { 936*c1d82b89SHans Verkuil .owner = THIS_MODULE, 937*c1d82b89SHans Verkuil .unlocked_ioctl = video_ioctl2, 938*c1d82b89SHans Verkuil .open = isi_open, 939*c1d82b89SHans Verkuil .release = isi_release, 940*c1d82b89SHans Verkuil .poll = vb2_fop_poll, 941*c1d82b89SHans Verkuil .mmap = vb2_fop_mmap, 942*c1d82b89SHans Verkuil .read = vb2_fop_read, 943*c1d82b89SHans Verkuil }; 944*c1d82b89SHans Verkuil 945*c1d82b89SHans Verkuil static int isi_set_default_fmt(struct atmel_isi *isi) 946*c1d82b89SHans Verkuil { 947*c1d82b89SHans Verkuil struct v4l2_format f = { 948*c1d82b89SHans Verkuil .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 949*c1d82b89SHans Verkuil .fmt.pix = { 950*c1d82b89SHans Verkuil .width = VGA_WIDTH, 951*c1d82b89SHans Verkuil .height = VGA_HEIGHT, 952*c1d82b89SHans Verkuil .field = V4L2_FIELD_NONE, 953*c1d82b89SHans Verkuil .pixelformat = isi->user_formats[0]->fourcc, 954*c1d82b89SHans Verkuil }, 955*c1d82b89SHans Verkuil }; 956*c1d82b89SHans Verkuil int ret; 957*c1d82b89SHans Verkuil 958*c1d82b89SHans Verkuil ret = isi_try_fmt(isi, &f, NULL); 959*c1d82b89SHans Verkuil if (ret) 960*c1d82b89SHans Verkuil return ret; 961*c1d82b89SHans Verkuil isi->current_fmt = isi->user_formats[0]; 962*c1d82b89SHans Verkuil isi->fmt = f; 963*c1d82b89SHans Verkuil return 0; 964*c1d82b89SHans Verkuil } 965*c1d82b89SHans Verkuil 966*c1d82b89SHans Verkuil static const struct isi_format isi_formats[] = { 967*c1d82b89SHans Verkuil { 968*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_YUYV, 969*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 970*c1d82b89SHans Verkuil .bpp = 2, 971*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_DEFAULT, 972*c1d82b89SHans Verkuil }, { 973*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_YUYV, 974*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, 975*c1d82b89SHans Verkuil .bpp = 2, 976*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_MODE_1, 977*c1d82b89SHans Verkuil }, { 978*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_YUYV, 979*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, 980*c1d82b89SHans Verkuil .bpp = 2, 981*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_MODE_2, 982*c1d82b89SHans Verkuil }, { 983*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_YUYV, 984*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, 985*c1d82b89SHans Verkuil .bpp = 2, 986*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_MODE_3, 987*c1d82b89SHans Verkuil }, { 988*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB565, 989*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 990*c1d82b89SHans Verkuil .bpp = 2, 991*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_MODE_2, 992*c1d82b89SHans Verkuil }, { 993*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB565, 994*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, 995*c1d82b89SHans Verkuil .bpp = 2, 996*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_MODE_3, 997*c1d82b89SHans Verkuil }, { 998*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB565, 999*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, 1000*c1d82b89SHans Verkuil .bpp = 2, 1001*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_DEFAULT, 1002*c1d82b89SHans Verkuil }, { 1003*c1d82b89SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB565, 1004*c1d82b89SHans Verkuil .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, 1005*c1d82b89SHans Verkuil .bpp = 2, 1006*c1d82b89SHans Verkuil .swap = ISI_CFG2_YCC_SWAP_MODE_1, 1007*c1d82b89SHans Verkuil }, 1008*c1d82b89SHans Verkuil }; 1009*c1d82b89SHans Verkuil 1010*c1d82b89SHans Verkuil static int isi_formats_init(struct atmel_isi *isi) 1011*c1d82b89SHans Verkuil { 1012*c1d82b89SHans Verkuil const struct isi_format *isi_fmts[ARRAY_SIZE(isi_formats)]; 1013*c1d82b89SHans Verkuil unsigned int num_fmts = 0, i, j; 1014*c1d82b89SHans Verkuil struct v4l2_subdev *subdev = isi->entity.subdev; 1015*c1d82b89SHans Verkuil struct v4l2_subdev_mbus_code_enum mbus_code = { 1016*c1d82b89SHans Verkuil .which = V4L2_SUBDEV_FORMAT_ACTIVE, 1017*c1d82b89SHans Verkuil }; 1018*c1d82b89SHans Verkuil 1019*c1d82b89SHans Verkuil while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, 1020*c1d82b89SHans Verkuil NULL, &mbus_code)) { 1021*c1d82b89SHans Verkuil for (i = 0; i < ARRAY_SIZE(isi_formats); i++) { 1022*c1d82b89SHans Verkuil if (isi_formats[i].mbus_code != mbus_code.code) 1023*c1d82b89SHans Verkuil continue; 1024*c1d82b89SHans Verkuil 1025*c1d82b89SHans Verkuil /* Code supported, have we got this fourcc yet? */ 1026*c1d82b89SHans Verkuil for (j = 0; j < num_fmts; j++) 1027*c1d82b89SHans Verkuil if (isi_fmts[j]->fourcc == isi_formats[i].fourcc) 1028*c1d82b89SHans Verkuil /* Already available */ 1029*c1d82b89SHans Verkuil break; 1030*c1d82b89SHans Verkuil if (j == num_fmts) 1031*c1d82b89SHans Verkuil /* new */ 1032*c1d82b89SHans Verkuil isi_fmts[num_fmts++] = isi_formats + i; 1033*c1d82b89SHans Verkuil } 1034*c1d82b89SHans Verkuil mbus_code.index++; 1035*c1d82b89SHans Verkuil } 1036*c1d82b89SHans Verkuil 1037*c1d82b89SHans Verkuil if (!num_fmts) 1038*c1d82b89SHans Verkuil return -ENXIO; 1039*c1d82b89SHans Verkuil 1040*c1d82b89SHans Verkuil isi->num_user_formats = num_fmts; 1041*c1d82b89SHans Verkuil isi->user_formats = devm_kcalloc(isi->dev, 1042*c1d82b89SHans Verkuil num_fmts, sizeof(struct isi_format *), 1043*c1d82b89SHans Verkuil GFP_KERNEL); 1044*c1d82b89SHans Verkuil if (!isi->user_formats) { 1045*c1d82b89SHans Verkuil dev_err(isi->dev, "could not allocate memory\n"); 1046*c1d82b89SHans Verkuil return -ENOMEM; 1047*c1d82b89SHans Verkuil } 1048*c1d82b89SHans Verkuil 1049*c1d82b89SHans Verkuil memcpy(isi->user_formats, isi_fmts, 1050*c1d82b89SHans Verkuil num_fmts * sizeof(struct isi_format *)); 1051*c1d82b89SHans Verkuil isi->current_fmt = isi->user_formats[0]; 1052*c1d82b89SHans Verkuil 1053*c1d82b89SHans Verkuil return 0; 1054*c1d82b89SHans Verkuil } 1055*c1d82b89SHans Verkuil 1056*c1d82b89SHans Verkuil static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) 1057*c1d82b89SHans Verkuil { 1058*c1d82b89SHans Verkuil struct atmel_isi *isi = notifier_to_isi(notifier); 1059*c1d82b89SHans Verkuil int ret; 1060*c1d82b89SHans Verkuil 1061*c1d82b89SHans Verkuil isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler; 1062*c1d82b89SHans Verkuil ret = isi_formats_init(isi); 1063*c1d82b89SHans Verkuil if (ret) { 1064*c1d82b89SHans Verkuil dev_err(isi->dev, "No supported mediabus format found\n"); 1065*c1d82b89SHans Verkuil return ret; 1066*c1d82b89SHans Verkuil } 1067*c1d82b89SHans Verkuil isi_camera_set_bus_param(isi); 1068*c1d82b89SHans Verkuil 1069*c1d82b89SHans Verkuil ret = isi_set_default_fmt(isi); 1070*c1d82b89SHans Verkuil if (ret) { 1071*c1d82b89SHans Verkuil dev_err(isi->dev, "Could not set default format\n"); 1072*c1d82b89SHans Verkuil return ret; 1073*c1d82b89SHans Verkuil } 1074*c1d82b89SHans Verkuil 1075*c1d82b89SHans Verkuil ret = video_register_device(isi->vdev, VFL_TYPE_GRABBER, -1); 1076*c1d82b89SHans Verkuil if (ret) { 1077*c1d82b89SHans Verkuil dev_err(isi->dev, "Failed to register video device\n"); 1078*c1d82b89SHans Verkuil return ret; 1079*c1d82b89SHans Verkuil } 1080*c1d82b89SHans Verkuil 1081*c1d82b89SHans Verkuil dev_dbg(isi->dev, "Device registered as %s\n", 1082*c1d82b89SHans Verkuil video_device_node_name(isi->vdev)); 1083*c1d82b89SHans Verkuil return 0; 1084*c1d82b89SHans Verkuil } 1085*c1d82b89SHans Verkuil 1086*c1d82b89SHans Verkuil static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier, 1087*c1d82b89SHans Verkuil struct v4l2_subdev *sd, 1088*c1d82b89SHans Verkuil struct v4l2_async_subdev *asd) 1089*c1d82b89SHans Verkuil { 1090*c1d82b89SHans Verkuil struct atmel_isi *isi = notifier_to_isi(notifier); 1091*c1d82b89SHans Verkuil 1092*c1d82b89SHans Verkuil dev_dbg(isi->dev, "Removing %s\n", video_device_node_name(isi->vdev)); 1093*c1d82b89SHans Verkuil 1094*c1d82b89SHans Verkuil /* Checks internaly if vdev have been init or not */ 1095*c1d82b89SHans Verkuil video_unregister_device(isi->vdev); 1096*c1d82b89SHans Verkuil } 1097*c1d82b89SHans Verkuil 1098*c1d82b89SHans Verkuil static int isi_graph_notify_bound(struct v4l2_async_notifier *notifier, 1099*c1d82b89SHans Verkuil struct v4l2_subdev *subdev, 1100*c1d82b89SHans Verkuil struct v4l2_async_subdev *asd) 1101*c1d82b89SHans Verkuil { 1102*c1d82b89SHans Verkuil struct atmel_isi *isi = notifier_to_isi(notifier); 1103*c1d82b89SHans Verkuil 1104*c1d82b89SHans Verkuil dev_dbg(isi->dev, "subdev %s bound\n", subdev->name); 1105*c1d82b89SHans Verkuil 1106*c1d82b89SHans Verkuil isi->entity.subdev = subdev; 1107*c1d82b89SHans Verkuil 1108*c1d82b89SHans Verkuil return 0; 1109*c1d82b89SHans Verkuil } 1110*c1d82b89SHans Verkuil 1111*c1d82b89SHans Verkuil static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) 1112*c1d82b89SHans Verkuil { 1113*c1d82b89SHans Verkuil struct device_node *ep = NULL; 1114*c1d82b89SHans Verkuil struct device_node *remote; 1115*c1d82b89SHans Verkuil 1116*c1d82b89SHans Verkuil while (1) { 1117*c1d82b89SHans Verkuil ep = of_graph_get_next_endpoint(node, ep); 1118*c1d82b89SHans Verkuil if (!ep) 1119*c1d82b89SHans Verkuil return -EINVAL; 1120*c1d82b89SHans Verkuil 1121*c1d82b89SHans Verkuil remote = of_graph_get_remote_port_parent(ep); 1122*c1d82b89SHans Verkuil if (!remote) { 1123*c1d82b89SHans Verkuil of_node_put(ep); 1124*c1d82b89SHans Verkuil return -EINVAL; 1125*c1d82b89SHans Verkuil } 1126*c1d82b89SHans Verkuil 1127*c1d82b89SHans Verkuil /* Remote node to connect */ 1128*c1d82b89SHans Verkuil isi->entity.node = remote; 1129*c1d82b89SHans Verkuil isi->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; 1130*c1d82b89SHans Verkuil isi->entity.asd.match.of.node = remote; 1131*c1d82b89SHans Verkuil return 0; 1132*c1d82b89SHans Verkuil } 1133*c1d82b89SHans Verkuil } 1134*c1d82b89SHans Verkuil 1135*c1d82b89SHans Verkuil static int isi_graph_init(struct atmel_isi *isi) 1136*c1d82b89SHans Verkuil { 1137*c1d82b89SHans Verkuil struct v4l2_async_subdev **subdevs = NULL; 1138*c1d82b89SHans Verkuil int ret; 1139*c1d82b89SHans Verkuil 1140*c1d82b89SHans Verkuil /* Parse the graph to extract a list of subdevice DT nodes. */ 1141*c1d82b89SHans Verkuil ret = isi_graph_parse(isi, isi->dev->of_node); 1142*c1d82b89SHans Verkuil if (ret < 0) { 1143*c1d82b89SHans Verkuil dev_err(isi->dev, "Graph parsing failed\n"); 1144*c1d82b89SHans Verkuil return ret; 1145*c1d82b89SHans Verkuil } 1146*c1d82b89SHans Verkuil 1147*c1d82b89SHans Verkuil /* Register the subdevices notifier. */ 1148*c1d82b89SHans Verkuil subdevs = devm_kzalloc(isi->dev, sizeof(*subdevs), GFP_KERNEL); 1149*c1d82b89SHans Verkuil if (subdevs == NULL) { 1150*c1d82b89SHans Verkuil of_node_put(isi->entity.node); 1151*c1d82b89SHans Verkuil return -ENOMEM; 1152*c1d82b89SHans Verkuil } 1153*c1d82b89SHans Verkuil 1154*c1d82b89SHans Verkuil subdevs[0] = &isi->entity.asd; 1155*c1d82b89SHans Verkuil 1156*c1d82b89SHans Verkuil isi->notifier.subdevs = subdevs; 1157*c1d82b89SHans Verkuil isi->notifier.num_subdevs = 1; 1158*c1d82b89SHans Verkuil isi->notifier.bound = isi_graph_notify_bound; 1159*c1d82b89SHans Verkuil isi->notifier.unbind = isi_graph_notify_unbind; 1160*c1d82b89SHans Verkuil isi->notifier.complete = isi_graph_notify_complete; 1161*c1d82b89SHans Verkuil 1162*c1d82b89SHans Verkuil ret = v4l2_async_notifier_register(&isi->v4l2_dev, &isi->notifier); 1163*c1d82b89SHans Verkuil if (ret < 0) { 1164*c1d82b89SHans Verkuil dev_err(isi->dev, "Notifier registration failed\n"); 1165*c1d82b89SHans Verkuil of_node_put(isi->entity.node); 1166*c1d82b89SHans Verkuil return ret; 1167*c1d82b89SHans Verkuil } 1168*c1d82b89SHans Verkuil 1169*c1d82b89SHans Verkuil return 0; 1170*c1d82b89SHans Verkuil } 1171*c1d82b89SHans Verkuil 1172*c1d82b89SHans Verkuil 1173*c1d82b89SHans Verkuil static int atmel_isi_probe(struct platform_device *pdev) 1174*c1d82b89SHans Verkuil { 1175*c1d82b89SHans Verkuil int irq; 1176*c1d82b89SHans Verkuil struct atmel_isi *isi; 1177*c1d82b89SHans Verkuil struct vb2_queue *q; 1178*c1d82b89SHans Verkuil struct resource *regs; 1179*c1d82b89SHans Verkuil int ret, i; 1180*c1d82b89SHans Verkuil 1181*c1d82b89SHans Verkuil isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); 1182*c1d82b89SHans Verkuil if (!isi) { 1183*c1d82b89SHans Verkuil dev_err(&pdev->dev, "Can't allocate interface!\n"); 1184*c1d82b89SHans Verkuil return -ENOMEM; 1185*c1d82b89SHans Verkuil } 1186*c1d82b89SHans Verkuil 1187*c1d82b89SHans Verkuil isi->pclk = devm_clk_get(&pdev->dev, "isi_clk"); 1188*c1d82b89SHans Verkuil if (IS_ERR(isi->pclk)) 1189*c1d82b89SHans Verkuil return PTR_ERR(isi->pclk); 1190*c1d82b89SHans Verkuil 1191*c1d82b89SHans Verkuil ret = atmel_isi_parse_dt(isi, pdev); 1192*c1d82b89SHans Verkuil if (ret) 1193*c1d82b89SHans Verkuil return ret; 1194*c1d82b89SHans Verkuil 1195*c1d82b89SHans Verkuil isi->active = NULL; 1196*c1d82b89SHans Verkuil isi->dev = &pdev->dev; 1197*c1d82b89SHans Verkuil mutex_init(&isi->lock); 1198*c1d82b89SHans Verkuil spin_lock_init(&isi->irqlock); 1199*c1d82b89SHans Verkuil INIT_LIST_HEAD(&isi->video_buffer_list); 1200*c1d82b89SHans Verkuil INIT_LIST_HEAD(&isi->dma_desc_head); 1201*c1d82b89SHans Verkuil 1202*c1d82b89SHans Verkuil q = &isi->queue; 1203*c1d82b89SHans Verkuil 1204*c1d82b89SHans Verkuil /* Initialize the top-level structure */ 1205*c1d82b89SHans Verkuil ret = v4l2_device_register(&pdev->dev, &isi->v4l2_dev); 1206*c1d82b89SHans Verkuil if (ret) 1207*c1d82b89SHans Verkuil return ret; 1208*c1d82b89SHans Verkuil 1209*c1d82b89SHans Verkuil isi->vdev = video_device_alloc(); 1210*c1d82b89SHans Verkuil if (isi->vdev == NULL) { 1211*c1d82b89SHans Verkuil ret = -ENOMEM; 1212*c1d82b89SHans Verkuil goto err_vdev_alloc; 1213*c1d82b89SHans Verkuil } 1214*c1d82b89SHans Verkuil 1215*c1d82b89SHans Verkuil /* video node */ 1216*c1d82b89SHans Verkuil isi->vdev->fops = &isi_fops; 1217*c1d82b89SHans Verkuil isi->vdev->v4l2_dev = &isi->v4l2_dev; 1218*c1d82b89SHans Verkuil isi->vdev->queue = &isi->queue; 1219*c1d82b89SHans Verkuil strlcpy(isi->vdev->name, KBUILD_MODNAME, sizeof(isi->vdev->name)); 1220*c1d82b89SHans Verkuil isi->vdev->release = video_device_release; 1221*c1d82b89SHans Verkuil isi->vdev->ioctl_ops = &isi_ioctl_ops; 1222*c1d82b89SHans Verkuil isi->vdev->lock = &isi->lock; 1223*c1d82b89SHans Verkuil isi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 1224*c1d82b89SHans Verkuil V4L2_CAP_READWRITE; 1225*c1d82b89SHans Verkuil video_set_drvdata(isi->vdev, isi); 1226*c1d82b89SHans Verkuil 1227*c1d82b89SHans Verkuil /* buffer queue */ 1228*c1d82b89SHans Verkuil q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1229*c1d82b89SHans Verkuil q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; 1230*c1d82b89SHans Verkuil q->lock = &isi->lock; 1231*c1d82b89SHans Verkuil q->drv_priv = isi; 1232*c1d82b89SHans Verkuil q->buf_struct_size = sizeof(struct frame_buffer); 1233*c1d82b89SHans Verkuil q->ops = &isi_video_qops; 1234*c1d82b89SHans Verkuil q->mem_ops = &vb2_dma_contig_memops; 1235*c1d82b89SHans Verkuil q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 1236*c1d82b89SHans Verkuil q->min_buffers_needed = 2; 1237*c1d82b89SHans Verkuil q->dev = &pdev->dev; 1238*c1d82b89SHans Verkuil 1239*c1d82b89SHans Verkuil ret = vb2_queue_init(q); 1240*c1d82b89SHans Verkuil if (ret < 0) { 1241*c1d82b89SHans Verkuil dev_err(&pdev->dev, "failed to initialize VB2 queue\n"); 1242*c1d82b89SHans Verkuil goto err_vb2_queue; 1243*c1d82b89SHans Verkuil } 1244*c1d82b89SHans Verkuil isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, 1245*c1d82b89SHans Verkuil sizeof(struct fbd) * VIDEO_MAX_FRAME, 1246*c1d82b89SHans Verkuil &isi->fb_descriptors_phys, 1247*c1d82b89SHans Verkuil GFP_KERNEL); 1248*c1d82b89SHans Verkuil if (!isi->p_fb_descriptors) { 1249*c1d82b89SHans Verkuil dev_err(&pdev->dev, "Can't allocate descriptors!\n"); 1250*c1d82b89SHans Verkuil ret = -ENOMEM; 1251*c1d82b89SHans Verkuil goto err_dma_alloc; 1252*c1d82b89SHans Verkuil } 1253*c1d82b89SHans Verkuil 1254*c1d82b89SHans Verkuil for (i = 0; i < VIDEO_MAX_FRAME; i++) { 1255*c1d82b89SHans Verkuil isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i; 1256*c1d82b89SHans Verkuil isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys + 1257*c1d82b89SHans Verkuil i * sizeof(struct fbd); 1258*c1d82b89SHans Verkuil list_add(&isi->dma_desc[i].list, &isi->dma_desc_head); 1259*c1d82b89SHans Verkuil } 1260*c1d82b89SHans Verkuil 1261*c1d82b89SHans Verkuil regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1262*c1d82b89SHans Verkuil isi->regs = devm_ioremap_resource(&pdev->dev, regs); 1263*c1d82b89SHans Verkuil if (IS_ERR(isi->regs)) { 1264*c1d82b89SHans Verkuil ret = PTR_ERR(isi->regs); 1265*c1d82b89SHans Verkuil goto err_ioremap; 1266*c1d82b89SHans Verkuil } 1267*c1d82b89SHans Verkuil 1268*c1d82b89SHans Verkuil if (isi->pdata.data_width_flags & ISI_DATAWIDTH_8) 1269*c1d82b89SHans Verkuil isi->width_flags = 1 << 7; 1270*c1d82b89SHans Verkuil if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) 1271*c1d82b89SHans Verkuil isi->width_flags |= 1 << 9; 1272*c1d82b89SHans Verkuil 1273*c1d82b89SHans Verkuil irq = platform_get_irq(pdev, 0); 1274*c1d82b89SHans Verkuil if (irq < 0) { 1275*c1d82b89SHans Verkuil ret = irq; 1276*c1d82b89SHans Verkuil goto err_req_irq; 1277*c1d82b89SHans Verkuil } 1278*c1d82b89SHans Verkuil 1279*c1d82b89SHans Verkuil ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi); 1280*c1d82b89SHans Verkuil if (ret) { 1281*c1d82b89SHans Verkuil dev_err(&pdev->dev, "Unable to request irq %d\n", irq); 1282*c1d82b89SHans Verkuil goto err_req_irq; 1283*c1d82b89SHans Verkuil } 1284*c1d82b89SHans Verkuil isi->irq = irq; 1285*c1d82b89SHans Verkuil 1286*c1d82b89SHans Verkuil ret = isi_graph_init(isi); 1287*c1d82b89SHans Verkuil if (ret < 0) 1288*c1d82b89SHans Verkuil goto err_req_irq; 1289*c1d82b89SHans Verkuil 1290*c1d82b89SHans Verkuil pm_suspend_ignore_children(&pdev->dev, true); 1291*c1d82b89SHans Verkuil pm_runtime_enable(&pdev->dev); 1292*c1d82b89SHans Verkuil platform_set_drvdata(pdev, isi); 1293*c1d82b89SHans Verkuil return 0; 1294*c1d82b89SHans Verkuil 1295*c1d82b89SHans Verkuil err_req_irq: 1296*c1d82b89SHans Verkuil err_ioremap: 1297*c1d82b89SHans Verkuil dma_free_coherent(&pdev->dev, 1298*c1d82b89SHans Verkuil sizeof(struct fbd) * VIDEO_MAX_FRAME, 1299*c1d82b89SHans Verkuil isi->p_fb_descriptors, 1300*c1d82b89SHans Verkuil isi->fb_descriptors_phys); 1301*c1d82b89SHans Verkuil err_dma_alloc: 1302*c1d82b89SHans Verkuil err_vb2_queue: 1303*c1d82b89SHans Verkuil video_device_release(isi->vdev); 1304*c1d82b89SHans Verkuil err_vdev_alloc: 1305*c1d82b89SHans Verkuil v4l2_device_unregister(&isi->v4l2_dev); 1306*c1d82b89SHans Verkuil 1307*c1d82b89SHans Verkuil return ret; 1308*c1d82b89SHans Verkuil } 1309*c1d82b89SHans Verkuil 1310*c1d82b89SHans Verkuil static int atmel_isi_remove(struct platform_device *pdev) 1311*c1d82b89SHans Verkuil { 1312*c1d82b89SHans Verkuil struct atmel_isi *isi = platform_get_drvdata(pdev); 1313*c1d82b89SHans Verkuil 1314*c1d82b89SHans Verkuil dma_free_coherent(&pdev->dev, 1315*c1d82b89SHans Verkuil sizeof(struct fbd) * VIDEO_MAX_FRAME, 1316*c1d82b89SHans Verkuil isi->p_fb_descriptors, 1317*c1d82b89SHans Verkuil isi->fb_descriptors_phys); 1318*c1d82b89SHans Verkuil pm_runtime_disable(&pdev->dev); 1319*c1d82b89SHans Verkuil v4l2_async_notifier_unregister(&isi->notifier); 1320*c1d82b89SHans Verkuil v4l2_device_unregister(&isi->v4l2_dev); 1321*c1d82b89SHans Verkuil 1322*c1d82b89SHans Verkuil return 0; 1323*c1d82b89SHans Verkuil } 1324*c1d82b89SHans Verkuil 1325*c1d82b89SHans Verkuil #ifdef CONFIG_PM 1326*c1d82b89SHans Verkuil static int atmel_isi_runtime_suspend(struct device *dev) 1327*c1d82b89SHans Verkuil { 1328*c1d82b89SHans Verkuil struct atmel_isi *isi = dev_get_drvdata(dev); 1329*c1d82b89SHans Verkuil 1330*c1d82b89SHans Verkuil clk_disable_unprepare(isi->pclk); 1331*c1d82b89SHans Verkuil 1332*c1d82b89SHans Verkuil return 0; 1333*c1d82b89SHans Verkuil } 1334*c1d82b89SHans Verkuil static int atmel_isi_runtime_resume(struct device *dev) 1335*c1d82b89SHans Verkuil { 1336*c1d82b89SHans Verkuil struct atmel_isi *isi = dev_get_drvdata(dev); 1337*c1d82b89SHans Verkuil 1338*c1d82b89SHans Verkuil return clk_prepare_enable(isi->pclk); 1339*c1d82b89SHans Verkuil } 1340*c1d82b89SHans Verkuil #endif /* CONFIG_PM */ 1341*c1d82b89SHans Verkuil 1342*c1d82b89SHans Verkuil static const struct dev_pm_ops atmel_isi_dev_pm_ops = { 1343*c1d82b89SHans Verkuil SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend, 1344*c1d82b89SHans Verkuil atmel_isi_runtime_resume, NULL) 1345*c1d82b89SHans Verkuil }; 1346*c1d82b89SHans Verkuil 1347*c1d82b89SHans Verkuil static const struct of_device_id atmel_isi_of_match[] = { 1348*c1d82b89SHans Verkuil { .compatible = "atmel,at91sam9g45-isi" }, 1349*c1d82b89SHans Verkuil { } 1350*c1d82b89SHans Verkuil }; 1351*c1d82b89SHans Verkuil MODULE_DEVICE_TABLE(of, atmel_isi_of_match); 1352*c1d82b89SHans Verkuil 1353*c1d82b89SHans Verkuil static struct platform_driver atmel_isi_driver = { 1354*c1d82b89SHans Verkuil .driver = { 1355*c1d82b89SHans Verkuil .name = "atmel_isi", 1356*c1d82b89SHans Verkuil .of_match_table = of_match_ptr(atmel_isi_of_match), 1357*c1d82b89SHans Verkuil .pm = &atmel_isi_dev_pm_ops, 1358*c1d82b89SHans Verkuil }, 1359*c1d82b89SHans Verkuil .probe = atmel_isi_probe, 1360*c1d82b89SHans Verkuil .remove = atmel_isi_remove, 1361*c1d82b89SHans Verkuil }; 1362*c1d82b89SHans Verkuil 1363*c1d82b89SHans Verkuil module_platform_driver(atmel_isi_driver); 1364*c1d82b89SHans Verkuil 1365*c1d82b89SHans Verkuil MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>"); 1366*c1d82b89SHans Verkuil MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux"); 1367*c1d82b89SHans Verkuil MODULE_LICENSE("GPL"); 1368*c1d82b89SHans Verkuil MODULE_SUPPORTED_DEVICE("video"); 1369