xref: /openbmc/linux/drivers/media/platform/atmel/atmel-isi.c (revision c1d82b89538032718cba30b5b1fe192dfb343212)
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, &current_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