1*9f257f50SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0
2*9f257f50SLaurent Pinchart /*
3*9f257f50SLaurent Pinchart  * V4L2 Capture CSI Subdev for Freescale i.MX6UL/L / i.MX7 SOC
4*9f257f50SLaurent Pinchart  *
5*9f257f50SLaurent Pinchart  * Copyright (c) 2019 Linaro Ltd
6*9f257f50SLaurent Pinchart  *
7*9f257f50SLaurent Pinchart  */
8*9f257f50SLaurent Pinchart 
9*9f257f50SLaurent Pinchart #include <linux/clk.h>
10*9f257f50SLaurent Pinchart #include <linux/delay.h>
11*9f257f50SLaurent Pinchart #include <linux/interrupt.h>
12*9f257f50SLaurent Pinchart #include <linux/mfd/syscon.h>
13*9f257f50SLaurent Pinchart #include <linux/module.h>
14*9f257f50SLaurent Pinchart #include <linux/of_device.h>
15*9f257f50SLaurent Pinchart #include <linux/of_graph.h>
16*9f257f50SLaurent Pinchart #include <linux/pinctrl/consumer.h>
17*9f257f50SLaurent Pinchart #include <linux/platform_device.h>
18*9f257f50SLaurent Pinchart #include <linux/regmap.h>
19*9f257f50SLaurent Pinchart #include <linux/slab.h>
20*9f257f50SLaurent Pinchart #include <linux/spinlock.h>
21*9f257f50SLaurent Pinchart #include <linux/types.h>
22*9f257f50SLaurent Pinchart 
23*9f257f50SLaurent Pinchart #include <media/v4l2-device.h>
24*9f257f50SLaurent Pinchart #include <media/v4l2-fwnode.h>
25*9f257f50SLaurent Pinchart #include <media/v4l2-ioctl.h>
26*9f257f50SLaurent Pinchart #include <media/v4l2-mc.h>
27*9f257f50SLaurent Pinchart #include <media/v4l2-subdev.h>
28*9f257f50SLaurent Pinchart #include <media/videobuf2-dma-contig.h>
29*9f257f50SLaurent Pinchart 
30*9f257f50SLaurent Pinchart #define IMX7_CSI_PAD_SINK		0
31*9f257f50SLaurent Pinchart #define IMX7_CSI_PAD_SRC		1
32*9f257f50SLaurent Pinchart #define IMX7_CSI_PADS_NUM		2
33*9f257f50SLaurent Pinchart 
34*9f257f50SLaurent Pinchart /* csi control reg 1 */
35*9f257f50SLaurent Pinchart #define BIT_SWAP16_EN			BIT(31)
36*9f257f50SLaurent Pinchart #define BIT_EXT_VSYNC			BIT(30)
37*9f257f50SLaurent Pinchart #define BIT_EOF_INT_EN			BIT(29)
38*9f257f50SLaurent Pinchart #define BIT_PRP_IF_EN			BIT(28)
39*9f257f50SLaurent Pinchart #define BIT_CCIR_MODE			BIT(27)
40*9f257f50SLaurent Pinchart #define BIT_COF_INT_EN			BIT(26)
41*9f257f50SLaurent Pinchart #define BIT_SF_OR_INTEN			BIT(25)
42*9f257f50SLaurent Pinchart #define BIT_RF_OR_INTEN			BIT(24)
43*9f257f50SLaurent Pinchart #define BIT_SFF_DMA_DONE_INTEN		BIT(22)
44*9f257f50SLaurent Pinchart #define BIT_STATFF_INTEN		BIT(21)
45*9f257f50SLaurent Pinchart #define BIT_FB2_DMA_DONE_INTEN		BIT(20)
46*9f257f50SLaurent Pinchart #define BIT_FB1_DMA_DONE_INTEN		BIT(19)
47*9f257f50SLaurent Pinchart #define BIT_RXFF_INTEN			BIT(18)
48*9f257f50SLaurent Pinchart #define BIT_SOF_POL			BIT(17)
49*9f257f50SLaurent Pinchart #define BIT_SOF_INTEN			BIT(16)
50*9f257f50SLaurent Pinchart #define BIT_MCLKDIV(n)			((n) << 12)
51*9f257f50SLaurent Pinchart #define BIT_MCLKDIV_MASK		(0xf << 12)
52*9f257f50SLaurent Pinchart #define BIT_HSYNC_POL			BIT(11)
53*9f257f50SLaurent Pinchart #define BIT_CCIR_EN			BIT(10)
54*9f257f50SLaurent Pinchart #define BIT_MCLKEN			BIT(9)
55*9f257f50SLaurent Pinchart #define BIT_FCC				BIT(8)
56*9f257f50SLaurent Pinchart #define BIT_PACK_DIR			BIT(7)
57*9f257f50SLaurent Pinchart #define BIT_CLR_STATFIFO		BIT(6)
58*9f257f50SLaurent Pinchart #define BIT_CLR_RXFIFO			BIT(5)
59*9f257f50SLaurent Pinchart #define BIT_GCLK_MODE			BIT(4)
60*9f257f50SLaurent Pinchart #define BIT_INV_DATA			BIT(3)
61*9f257f50SLaurent Pinchart #define BIT_INV_PCLK			BIT(2)
62*9f257f50SLaurent Pinchart #define BIT_REDGE			BIT(1)
63*9f257f50SLaurent Pinchart #define BIT_PIXEL_BIT			BIT(0)
64*9f257f50SLaurent Pinchart 
65*9f257f50SLaurent Pinchart /* control reg 2 */
66*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_INCR4	(1 << 30)
67*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_INCR8	(2 << 30)
68*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_INCR16	(3 << 30)
69*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_MASK	(3 << 30)
70*9f257f50SLaurent Pinchart 
71*9f257f50SLaurent Pinchart /* control reg 3 */
72*9f257f50SLaurent Pinchart #define BIT_FRMCNT(n)			((n) << 16)
73*9f257f50SLaurent Pinchart #define BIT_FRMCNT_MASK			(0xffff << 16)
74*9f257f50SLaurent Pinchart #define BIT_FRMCNT_RST			BIT(15)
75*9f257f50SLaurent Pinchart #define BIT_DMA_REFLASH_RFF		BIT(14)
76*9f257f50SLaurent Pinchart #define BIT_DMA_REFLASH_SFF		BIT(13)
77*9f257f50SLaurent Pinchart #define BIT_DMA_REQ_EN_RFF		BIT(12)
78*9f257f50SLaurent Pinchart #define BIT_DMA_REQ_EN_SFF		BIT(11)
79*9f257f50SLaurent Pinchart #define BIT_STATFF_LEVEL(n)		((n) << 8)
80*9f257f50SLaurent Pinchart #define BIT_STATFF_LEVEL_MASK		(0x7 << 8)
81*9f257f50SLaurent Pinchart #define BIT_HRESP_ERR_EN		BIT(7)
82*9f257f50SLaurent Pinchart #define BIT_RXFF_LEVEL(n)		((n) << 4)
83*9f257f50SLaurent Pinchart #define BIT_RXFF_LEVEL_MASK		(0x7 << 4)
84*9f257f50SLaurent Pinchart #define BIT_TWO_8BIT_SENSOR		BIT(3)
85*9f257f50SLaurent Pinchart #define BIT_ZERO_PACK_EN		BIT(2)
86*9f257f50SLaurent Pinchart #define BIT_ECC_INT_EN			BIT(1)
87*9f257f50SLaurent Pinchart #define BIT_ECC_AUTO_EN			BIT(0)
88*9f257f50SLaurent Pinchart 
89*9f257f50SLaurent Pinchart /* csi status reg */
90*9f257f50SLaurent Pinchart #define BIT_ADDR_CH_ERR_INT		BIT(28)
91*9f257f50SLaurent Pinchart #define BIT_FIELD0_INT			BIT(27)
92*9f257f50SLaurent Pinchart #define BIT_FIELD1_INT			BIT(26)
93*9f257f50SLaurent Pinchart #define BIT_SFF_OR_INT			BIT(25)
94*9f257f50SLaurent Pinchart #define BIT_RFF_OR_INT			BIT(24)
95*9f257f50SLaurent Pinchart #define BIT_DMA_TSF_DONE_SFF		BIT(22)
96*9f257f50SLaurent Pinchart #define BIT_STATFF_INT			BIT(21)
97*9f257f50SLaurent Pinchart #define BIT_DMA_TSF_DONE_FB2		BIT(20)
98*9f257f50SLaurent Pinchart #define BIT_DMA_TSF_DONE_FB1		BIT(19)
99*9f257f50SLaurent Pinchart #define BIT_RXFF_INT			BIT(18)
100*9f257f50SLaurent Pinchart #define BIT_EOF_INT			BIT(17)
101*9f257f50SLaurent Pinchart #define BIT_SOF_INT			BIT(16)
102*9f257f50SLaurent Pinchart #define BIT_F2_INT			BIT(15)
103*9f257f50SLaurent Pinchart #define BIT_F1_INT			BIT(14)
104*9f257f50SLaurent Pinchart #define BIT_COF_INT			BIT(13)
105*9f257f50SLaurent Pinchart #define BIT_HRESP_ERR_INT		BIT(7)
106*9f257f50SLaurent Pinchart #define BIT_ECC_INT			BIT(1)
107*9f257f50SLaurent Pinchart #define BIT_DRDY			BIT(0)
108*9f257f50SLaurent Pinchart 
109*9f257f50SLaurent Pinchart /* csi image parameter reg */
110*9f257f50SLaurent Pinchart #define BIT_IMAGE_WIDTH(n)		((n) << 16)
111*9f257f50SLaurent Pinchart #define BIT_IMAGE_HEIGHT(n)		(n)
112*9f257f50SLaurent Pinchart 
113*9f257f50SLaurent Pinchart /* csi control reg 18 */
114*9f257f50SLaurent Pinchart #define BIT_CSI_HW_ENABLE		BIT(31)
115*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW8	(0x2a << 25)
116*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW10	(0x2b << 25)
117*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW12	(0x2c << 25)
118*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW14	(0x2d << 25)
119*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_YUV422_8B	(0x1e << 25)
120*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_MASK	(0x3f << 25)
121*9f257f50SLaurent Pinchart #define BIT_DATA_FROM_MIPI		BIT(22)
122*9f257f50SLaurent Pinchart #define BIT_MIPI_YU_SWAP		BIT(21)
123*9f257f50SLaurent Pinchart #define BIT_MIPI_DOUBLE_CMPNT		BIT(20)
124*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_FIRST_FRAME	(0 << 18)
125*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_CSI_EN		(1 << 18)
126*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_SECOND_FRAME	(2 << 18)
127*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_ON_DATA		(3 << 18)
128*9f257f50SLaurent Pinchart #define BIT_BASEADDR_CHG_ERR_EN		BIT(9)
129*9f257f50SLaurent Pinchart #define BIT_BASEADDR_SWITCH_SEL		BIT(5)
130*9f257f50SLaurent Pinchart #define BIT_BASEADDR_SWITCH_EN		BIT(4)
131*9f257f50SLaurent Pinchart #define BIT_PARALLEL24_EN		BIT(3)
132*9f257f50SLaurent Pinchart #define BIT_DEINTERLACE_EN		BIT(2)
133*9f257f50SLaurent Pinchart #define BIT_TVDECODER_IN_EN		BIT(1)
134*9f257f50SLaurent Pinchart #define BIT_NTSC_EN			BIT(0)
135*9f257f50SLaurent Pinchart 
136*9f257f50SLaurent Pinchart #define CSI_MCLK_VF			1
137*9f257f50SLaurent Pinchart #define CSI_MCLK_ENC			2
138*9f257f50SLaurent Pinchart #define CSI_MCLK_RAW			4
139*9f257f50SLaurent Pinchart #define CSI_MCLK_I2C			8
140*9f257f50SLaurent Pinchart 
141*9f257f50SLaurent Pinchart #define CSI_CSICR1			0x00
142*9f257f50SLaurent Pinchart #define CSI_CSICR2			0x04
143*9f257f50SLaurent Pinchart #define CSI_CSICR3			0x08
144*9f257f50SLaurent Pinchart #define CSI_STATFIFO			0x0c
145*9f257f50SLaurent Pinchart #define CSI_CSIRXFIFO			0x10
146*9f257f50SLaurent Pinchart #define CSI_CSIRXCNT			0x14
147*9f257f50SLaurent Pinchart #define CSI_CSISR			0x18
148*9f257f50SLaurent Pinchart 
149*9f257f50SLaurent Pinchart #define CSI_CSIDBG			0x1c
150*9f257f50SLaurent Pinchart #define CSI_CSIDMASA_STATFIFO		0x20
151*9f257f50SLaurent Pinchart #define CSI_CSIDMATS_STATFIFO		0x24
152*9f257f50SLaurent Pinchart #define CSI_CSIDMASA_FB1		0x28
153*9f257f50SLaurent Pinchart #define CSI_CSIDMASA_FB2		0x2c
154*9f257f50SLaurent Pinchart #define CSI_CSIFBUF_PARA		0x30
155*9f257f50SLaurent Pinchart #define CSI_CSIIMAG_PARA		0x34
156*9f257f50SLaurent Pinchart 
157*9f257f50SLaurent Pinchart #define CSI_CSICR18			0x48
158*9f257f50SLaurent Pinchart #define CSI_CSICR19			0x4c
159*9f257f50SLaurent Pinchart 
160*9f257f50SLaurent Pinchart #define IMX7_CSI_VIDEO_NAME		"imx-capture"
161*9f257f50SLaurent Pinchart /* In bytes, per queue */
162*9f257f50SLaurent Pinchart #define IMX7_CSI_VIDEO_MEM_LIMIT	SZ_512M
163*9f257f50SLaurent Pinchart #define IMX7_CSI_VIDEO_EOF_TIMEOUT	2000
164*9f257f50SLaurent Pinchart 
165*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_MBUS_CODE		MEDIA_BUS_FMT_UYVY8_2X8
166*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_PIX_FORMAT		V4L2_PIX_FMT_UYVY
167*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_PIX_WIDTH		640
168*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_PIX_HEIGHT		480
169*9f257f50SLaurent Pinchart 
170*9f257f50SLaurent Pinchart enum imx_csi_model {
171*9f257f50SLaurent Pinchart 	IMX7_CSI_IMX7 = 0,
172*9f257f50SLaurent Pinchart 	IMX7_CSI_IMX8MQ,
173*9f257f50SLaurent Pinchart };
174*9f257f50SLaurent Pinchart 
175*9f257f50SLaurent Pinchart struct imx7_csi_pixfmt {
176*9f257f50SLaurent Pinchart 	/* the in-memory FourCC pixel format */
177*9f257f50SLaurent Pinchart 	u32     fourcc;
178*9f257f50SLaurent Pinchart 	/*
179*9f257f50SLaurent Pinchart 	 * the set of equivalent media bus codes for the fourcc.
180*9f257f50SLaurent Pinchart 	 * NOTE! codes pointer is NULL for in-memory-only formats.
181*9f257f50SLaurent Pinchart 	 */
182*9f257f50SLaurent Pinchart 	const u32 *codes;
183*9f257f50SLaurent Pinchart 	int     bpp;     /* total bpp */
184*9f257f50SLaurent Pinchart 	bool	yuv;
185*9f257f50SLaurent Pinchart };
186*9f257f50SLaurent Pinchart 
187*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer {
188*9f257f50SLaurent Pinchart 	struct vb2_v4l2_buffer vbuf;
189*9f257f50SLaurent Pinchart 	struct list_head list;
190*9f257f50SLaurent Pinchart };
191*9f257f50SLaurent Pinchart 
192*9f257f50SLaurent Pinchart static inline struct imx7_csi_vb2_buffer *
193*9f257f50SLaurent Pinchart to_imx7_csi_vb2_buffer(struct vb2_buffer *vb)
194*9f257f50SLaurent Pinchart {
195*9f257f50SLaurent Pinchart 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
196*9f257f50SLaurent Pinchart 
197*9f257f50SLaurent Pinchart 	return container_of(vbuf, struct imx7_csi_vb2_buffer, vbuf);
198*9f257f50SLaurent Pinchart }
199*9f257f50SLaurent Pinchart 
200*9f257f50SLaurent Pinchart struct imx7_csi_dma_buf {
201*9f257f50SLaurent Pinchart 	void *virt;
202*9f257f50SLaurent Pinchart 	dma_addr_t dma_addr;
203*9f257f50SLaurent Pinchart 	unsigned long len;
204*9f257f50SLaurent Pinchart };
205*9f257f50SLaurent Pinchart 
206*9f257f50SLaurent Pinchart struct imx7_csi {
207*9f257f50SLaurent Pinchart 	struct device *dev;
208*9f257f50SLaurent Pinchart 
209*9f257f50SLaurent Pinchart 	/* Resources and locks */
210*9f257f50SLaurent Pinchart 	void __iomem *regbase;
211*9f257f50SLaurent Pinchart 	int irq;
212*9f257f50SLaurent Pinchart 	struct clk *mclk;
213*9f257f50SLaurent Pinchart 
214*9f257f50SLaurent Pinchart 	struct mutex lock; /* Protects is_streaming, format_mbus, cc */
215*9f257f50SLaurent Pinchart 	spinlock_t irqlock; /* Protects last_eof */
216*9f257f50SLaurent Pinchart 
217*9f257f50SLaurent Pinchart 	/* Media and V4L2 device */
218*9f257f50SLaurent Pinchart 	struct media_device mdev;
219*9f257f50SLaurent Pinchart 	struct v4l2_device v4l2_dev;
220*9f257f50SLaurent Pinchart 	struct v4l2_async_notifier notifier;
221*9f257f50SLaurent Pinchart 	struct media_pipeline pipe;
222*9f257f50SLaurent Pinchart 
223*9f257f50SLaurent Pinchart 	struct v4l2_subdev *src_sd;
224*9f257f50SLaurent Pinchart 	bool is_csi2;
225*9f257f50SLaurent Pinchart 
226*9f257f50SLaurent Pinchart 	/* V4L2 subdev */
227*9f257f50SLaurent Pinchart 	struct v4l2_subdev sd;
228*9f257f50SLaurent Pinchart 	struct media_pad pad[IMX7_CSI_PADS_NUM];
229*9f257f50SLaurent Pinchart 
230*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM];
231*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc[IMX7_CSI_PADS_NUM];
232*9f257f50SLaurent Pinchart 
233*9f257f50SLaurent Pinchart 	/* Video device */
234*9f257f50SLaurent Pinchart 	struct video_device *vdev;		/* Video device */
235*9f257f50SLaurent Pinchart 	struct media_pad vdev_pad;		/* Video device pad */
236*9f257f50SLaurent Pinchart 
237*9f257f50SLaurent Pinchart 	struct v4l2_pix_format vdev_fmt;	/* The user format */
238*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *vdev_cc;
239*9f257f50SLaurent Pinchart 	struct v4l2_rect vdev_compose;		/* The compose rectangle */
240*9f257f50SLaurent Pinchart 
241*9f257f50SLaurent Pinchart 	struct mutex vdev_mutex;		/* Protect vdev operations */
242*9f257f50SLaurent Pinchart 
243*9f257f50SLaurent Pinchart 	struct vb2_queue q;			/* The videobuf2 queue */
244*9f257f50SLaurent Pinchart 	struct list_head ready_q;		/* List of queued buffers */
245*9f257f50SLaurent Pinchart 	spinlock_t q_lock;			/* Protect ready_q */
246*9f257f50SLaurent Pinchart 
247*9f257f50SLaurent Pinchart 	/* Buffers and streaming state */
248*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *active_vb2_buf[2];
249*9f257f50SLaurent Pinchart 	struct imx7_csi_dma_buf underrun_buf;
250*9f257f50SLaurent Pinchart 
251*9f257f50SLaurent Pinchart 	bool is_streaming;
252*9f257f50SLaurent Pinchart 	int buf_num;
253*9f257f50SLaurent Pinchart 	u32 frame_sequence;
254*9f257f50SLaurent Pinchart 
255*9f257f50SLaurent Pinchart 	bool last_eof;
256*9f257f50SLaurent Pinchart 	struct completion last_eof_completion;
257*9f257f50SLaurent Pinchart 
258*9f257f50SLaurent Pinchart 	enum imx_csi_model model;
259*9f257f50SLaurent Pinchart };
260*9f257f50SLaurent Pinchart 
261*9f257f50SLaurent Pinchart static struct imx7_csi *
262*9f257f50SLaurent Pinchart imx7_csi_notifier_to_dev(struct v4l2_async_notifier *n)
263*9f257f50SLaurent Pinchart {
264*9f257f50SLaurent Pinchart 	return container_of(n, struct imx7_csi, notifier);
265*9f257f50SLaurent Pinchart }
266*9f257f50SLaurent Pinchart 
267*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
268*9f257f50SLaurent Pinchart  * Hardware Configuration
269*9f257f50SLaurent Pinchart  */
270*9f257f50SLaurent Pinchart 
271*9f257f50SLaurent Pinchart static u32 imx7_csi_reg_read(struct imx7_csi *csi, unsigned int offset)
272*9f257f50SLaurent Pinchart {
273*9f257f50SLaurent Pinchart 	return readl(csi->regbase + offset);
274*9f257f50SLaurent Pinchart }
275*9f257f50SLaurent Pinchart 
276*9f257f50SLaurent Pinchart static void imx7_csi_reg_write(struct imx7_csi *csi, unsigned int value,
277*9f257f50SLaurent Pinchart 			       unsigned int offset)
278*9f257f50SLaurent Pinchart {
279*9f257f50SLaurent Pinchart 	writel(value, csi->regbase + offset);
280*9f257f50SLaurent Pinchart }
281*9f257f50SLaurent Pinchart 
282*9f257f50SLaurent Pinchart static u32 imx7_csi_irq_clear(struct imx7_csi *csi)
283*9f257f50SLaurent Pinchart {
284*9f257f50SLaurent Pinchart 	u32 isr;
285*9f257f50SLaurent Pinchart 
286*9f257f50SLaurent Pinchart 	isr = imx7_csi_reg_read(csi, CSI_CSISR);
287*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, isr, CSI_CSISR);
288*9f257f50SLaurent Pinchart 
289*9f257f50SLaurent Pinchart 	return isr;
290*9f257f50SLaurent Pinchart }
291*9f257f50SLaurent Pinchart 
292*9f257f50SLaurent Pinchart static void imx7_csi_init_default(struct imx7_csi *csi)
293*9f257f50SLaurent Pinchart {
294*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE |
295*9f257f50SLaurent Pinchart 			   BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) |
296*9f257f50SLaurent Pinchart 			   BIT_MCLKEN, CSI_CSICR1);
297*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, 0, CSI_CSICR2);
298*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, BIT_FRMCNT_RST, CSI_CSICR3);
299*9f257f50SLaurent Pinchart 
300*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(IMX7_CSI_DEF_PIX_WIDTH) |
301*9f257f50SLaurent Pinchart 			   BIT_IMAGE_HEIGHT(IMX7_CSI_DEF_PIX_HEIGHT),
302*9f257f50SLaurent Pinchart 			   CSI_CSIIMAG_PARA);
303*9f257f50SLaurent Pinchart 
304*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, BIT_DMA_REFLASH_RFF, CSI_CSICR3);
305*9f257f50SLaurent Pinchart }
306*9f257f50SLaurent Pinchart 
307*9f257f50SLaurent Pinchart static void imx7_csi_hw_enable_irq(struct imx7_csi *csi)
308*9f257f50SLaurent Pinchart {
309*9f257f50SLaurent Pinchart 	u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
310*9f257f50SLaurent Pinchart 
311*9f257f50SLaurent Pinchart 	cr1 |= BIT_RFF_OR_INT;
312*9f257f50SLaurent Pinchart 	cr1 |= BIT_FB1_DMA_DONE_INTEN;
313*9f257f50SLaurent Pinchart 	cr1 |= BIT_FB2_DMA_DONE_INTEN;
314*9f257f50SLaurent Pinchart 
315*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
316*9f257f50SLaurent Pinchart }
317*9f257f50SLaurent Pinchart 
318*9f257f50SLaurent Pinchart static void imx7_csi_hw_disable_irq(struct imx7_csi *csi)
319*9f257f50SLaurent Pinchart {
320*9f257f50SLaurent Pinchart 	u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
321*9f257f50SLaurent Pinchart 
322*9f257f50SLaurent Pinchart 	cr1 &= ~BIT_RFF_OR_INT;
323*9f257f50SLaurent Pinchart 	cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
324*9f257f50SLaurent Pinchart 	cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
325*9f257f50SLaurent Pinchart 
326*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
327*9f257f50SLaurent Pinchart }
328*9f257f50SLaurent Pinchart 
329*9f257f50SLaurent Pinchart static void imx7_csi_hw_enable(struct imx7_csi *csi)
330*9f257f50SLaurent Pinchart {
331*9f257f50SLaurent Pinchart 	u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18);
332*9f257f50SLaurent Pinchart 
333*9f257f50SLaurent Pinchart 	cr |= BIT_CSI_HW_ENABLE;
334*9f257f50SLaurent Pinchart 
335*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr, CSI_CSICR18);
336*9f257f50SLaurent Pinchart }
337*9f257f50SLaurent Pinchart 
338*9f257f50SLaurent Pinchart static void imx7_csi_hw_disable(struct imx7_csi *csi)
339*9f257f50SLaurent Pinchart {
340*9f257f50SLaurent Pinchart 	u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18);
341*9f257f50SLaurent Pinchart 
342*9f257f50SLaurent Pinchart 	cr &= ~BIT_CSI_HW_ENABLE;
343*9f257f50SLaurent Pinchart 
344*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr, CSI_CSICR18);
345*9f257f50SLaurent Pinchart }
346*9f257f50SLaurent Pinchart 
347*9f257f50SLaurent Pinchart static void imx7_csi_dma_reflash(struct imx7_csi *csi)
348*9f257f50SLaurent Pinchart {
349*9f257f50SLaurent Pinchart 	u32 cr3;
350*9f257f50SLaurent Pinchart 
351*9f257f50SLaurent Pinchart 	cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
352*9f257f50SLaurent Pinchart 	cr3 |= BIT_DMA_REFLASH_RFF;
353*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
354*9f257f50SLaurent Pinchart }
355*9f257f50SLaurent Pinchart 
356*9f257f50SLaurent Pinchart static void imx7_csi_rx_fifo_clear(struct imx7_csi *csi)
357*9f257f50SLaurent Pinchart {
358*9f257f50SLaurent Pinchart 	u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1) & ~BIT_FCC;
359*9f257f50SLaurent Pinchart 
360*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
361*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
362*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr1 | BIT_FCC, CSI_CSICR1);
363*9f257f50SLaurent Pinchart }
364*9f257f50SLaurent Pinchart 
365*9f257f50SLaurent Pinchart static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi)
366*9f257f50SLaurent Pinchart {
367*9f257f50SLaurent Pinchart 	u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
368*9f257f50SLaurent Pinchart 
369*9f257f50SLaurent Pinchart 	cr3 |= BIT_DMA_REQ_EN_RFF;
370*9f257f50SLaurent Pinchart 	cr3 |= BIT_HRESP_ERR_EN;
371*9f257f50SLaurent Pinchart 	cr3 &= ~BIT_RXFF_LEVEL_MASK;
372*9f257f50SLaurent Pinchart 	cr3 |= BIT_RXFF_LEVEL(2);
373*9f257f50SLaurent Pinchart 
374*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
375*9f257f50SLaurent Pinchart }
376*9f257f50SLaurent Pinchart 
377*9f257f50SLaurent Pinchart static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi)
378*9f257f50SLaurent Pinchart {
379*9f257f50SLaurent Pinchart 	u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
380*9f257f50SLaurent Pinchart 
381*9f257f50SLaurent Pinchart 	cr3 &= ~BIT_DMA_REQ_EN_RFF;
382*9f257f50SLaurent Pinchart 	cr3 &= ~BIT_HRESP_ERR_EN;
383*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
384*9f257f50SLaurent Pinchart }
385*9f257f50SLaurent Pinchart 
386*9f257f50SLaurent Pinchart static void imx7_csi_update_buf(struct imx7_csi *csi, dma_addr_t dma_addr,
387*9f257f50SLaurent Pinchart 				int buf_num)
388*9f257f50SLaurent Pinchart {
389*9f257f50SLaurent Pinchart 	if (buf_num == 1)
390*9f257f50SLaurent Pinchart 		imx7_csi_reg_write(csi, dma_addr, CSI_CSIDMASA_FB2);
391*9f257f50SLaurent Pinchart 	else
392*9f257f50SLaurent Pinchart 		imx7_csi_reg_write(csi, dma_addr, CSI_CSIDMASA_FB1);
393*9f257f50SLaurent Pinchart }
394*9f257f50SLaurent Pinchart 
395*9f257f50SLaurent Pinchart static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi);
396*9f257f50SLaurent Pinchart 
397*9f257f50SLaurent Pinchart static void imx7_csi_setup_vb2_buf(struct imx7_csi *csi)
398*9f257f50SLaurent Pinchart {
399*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *buf;
400*9f257f50SLaurent Pinchart 	struct vb2_buffer *vb2_buf;
401*9f257f50SLaurent Pinchart 	int i;
402*9f257f50SLaurent Pinchart 
403*9f257f50SLaurent Pinchart 	for (i = 0; i < 2; i++) {
404*9f257f50SLaurent Pinchart 		dma_addr_t dma_addr;
405*9f257f50SLaurent Pinchart 
406*9f257f50SLaurent Pinchart 		buf = imx7_csi_video_next_buf(csi);
407*9f257f50SLaurent Pinchart 		if (buf) {
408*9f257f50SLaurent Pinchart 			csi->active_vb2_buf[i] = buf;
409*9f257f50SLaurent Pinchart 			vb2_buf = &buf->vbuf.vb2_buf;
410*9f257f50SLaurent Pinchart 			dma_addr = vb2_dma_contig_plane_dma_addr(vb2_buf, 0);
411*9f257f50SLaurent Pinchart 		} else {
412*9f257f50SLaurent Pinchart 			csi->active_vb2_buf[i] = NULL;
413*9f257f50SLaurent Pinchart 			dma_addr = csi->underrun_buf.dma_addr;
414*9f257f50SLaurent Pinchart 		}
415*9f257f50SLaurent Pinchart 
416*9f257f50SLaurent Pinchart 		imx7_csi_update_buf(csi, dma_addr, i);
417*9f257f50SLaurent Pinchart 	}
418*9f257f50SLaurent Pinchart }
419*9f257f50SLaurent Pinchart 
420*9f257f50SLaurent Pinchart static void imx7_csi_dma_unsetup_vb2_buf(struct imx7_csi *csi,
421*9f257f50SLaurent Pinchart 					 enum vb2_buffer_state return_status)
422*9f257f50SLaurent Pinchart {
423*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *buf;
424*9f257f50SLaurent Pinchart 	int i;
425*9f257f50SLaurent Pinchart 
426*9f257f50SLaurent Pinchart 	/* return any remaining active frames with return_status */
427*9f257f50SLaurent Pinchart 	for (i = 0; i < 2; i++) {
428*9f257f50SLaurent Pinchart 		buf = csi->active_vb2_buf[i];
429*9f257f50SLaurent Pinchart 		if (buf) {
430*9f257f50SLaurent Pinchart 			struct vb2_buffer *vb = &buf->vbuf.vb2_buf;
431*9f257f50SLaurent Pinchart 
432*9f257f50SLaurent Pinchart 			vb->timestamp = ktime_get_ns();
433*9f257f50SLaurent Pinchart 			vb2_buffer_done(vb, return_status);
434*9f257f50SLaurent Pinchart 			csi->active_vb2_buf[i] = NULL;
435*9f257f50SLaurent Pinchart 		}
436*9f257f50SLaurent Pinchart 	}
437*9f257f50SLaurent Pinchart }
438*9f257f50SLaurent Pinchart 
439*9f257f50SLaurent Pinchart static void imx7_csi_free_dma_buf(struct imx7_csi *csi,
440*9f257f50SLaurent Pinchart 				  struct imx7_csi_dma_buf *buf)
441*9f257f50SLaurent Pinchart {
442*9f257f50SLaurent Pinchart 	if (buf->virt)
443*9f257f50SLaurent Pinchart 		dma_free_coherent(csi->dev, buf->len, buf->virt, buf->dma_addr);
444*9f257f50SLaurent Pinchart 
445*9f257f50SLaurent Pinchart 	buf->virt = NULL;
446*9f257f50SLaurent Pinchart 	buf->dma_addr = 0;
447*9f257f50SLaurent Pinchart }
448*9f257f50SLaurent Pinchart 
449*9f257f50SLaurent Pinchart static int imx7_csi_alloc_dma_buf(struct imx7_csi *csi,
450*9f257f50SLaurent Pinchart 				  struct imx7_csi_dma_buf *buf, int size)
451*9f257f50SLaurent Pinchart {
452*9f257f50SLaurent Pinchart 	imx7_csi_free_dma_buf(csi, buf);
453*9f257f50SLaurent Pinchart 
454*9f257f50SLaurent Pinchart 	buf->len = PAGE_ALIGN(size);
455*9f257f50SLaurent Pinchart 	buf->virt = dma_alloc_coherent(csi->dev, buf->len, &buf->dma_addr,
456*9f257f50SLaurent Pinchart 				       GFP_DMA | GFP_KERNEL);
457*9f257f50SLaurent Pinchart 	if (!buf->virt)
458*9f257f50SLaurent Pinchart 		return -ENOMEM;
459*9f257f50SLaurent Pinchart 
460*9f257f50SLaurent Pinchart 	return 0;
461*9f257f50SLaurent Pinchart }
462*9f257f50SLaurent Pinchart 
463*9f257f50SLaurent Pinchart static int imx7_csi_dma_setup(struct imx7_csi *csi)
464*9f257f50SLaurent Pinchart {
465*9f257f50SLaurent Pinchart 	int ret;
466*9f257f50SLaurent Pinchart 
467*9f257f50SLaurent Pinchart 	ret = imx7_csi_alloc_dma_buf(csi, &csi->underrun_buf,
468*9f257f50SLaurent Pinchart 				     csi->vdev_fmt.sizeimage);
469*9f257f50SLaurent Pinchart 	if (ret < 0) {
470*9f257f50SLaurent Pinchart 		v4l2_warn(&csi->sd, "consider increasing the CMA area\n");
471*9f257f50SLaurent Pinchart 		return ret;
472*9f257f50SLaurent Pinchart 	}
473*9f257f50SLaurent Pinchart 
474*9f257f50SLaurent Pinchart 	csi->frame_sequence = 0;
475*9f257f50SLaurent Pinchart 	csi->last_eof = false;
476*9f257f50SLaurent Pinchart 	init_completion(&csi->last_eof_completion);
477*9f257f50SLaurent Pinchart 
478*9f257f50SLaurent Pinchart 	imx7_csi_setup_vb2_buf(csi);
479*9f257f50SLaurent Pinchart 
480*9f257f50SLaurent Pinchart 	return 0;
481*9f257f50SLaurent Pinchart }
482*9f257f50SLaurent Pinchart 
483*9f257f50SLaurent Pinchart static void imx7_csi_dma_cleanup(struct imx7_csi *csi,
484*9f257f50SLaurent Pinchart 				 enum vb2_buffer_state return_status)
485*9f257f50SLaurent Pinchart {
486*9f257f50SLaurent Pinchart 	imx7_csi_dma_unsetup_vb2_buf(csi, return_status);
487*9f257f50SLaurent Pinchart 	imx7_csi_free_dma_buf(csi, &csi->underrun_buf);
488*9f257f50SLaurent Pinchart }
489*9f257f50SLaurent Pinchart 
490*9f257f50SLaurent Pinchart static void imx7_csi_dma_stop(struct imx7_csi *csi)
491*9f257f50SLaurent Pinchart {
492*9f257f50SLaurent Pinchart 	unsigned long timeout_jiffies;
493*9f257f50SLaurent Pinchart 	unsigned long flags;
494*9f257f50SLaurent Pinchart 	int ret;
495*9f257f50SLaurent Pinchart 
496*9f257f50SLaurent Pinchart 	/* mark next EOF interrupt as the last before stream off */
497*9f257f50SLaurent Pinchart 	spin_lock_irqsave(&csi->irqlock, flags);
498*9f257f50SLaurent Pinchart 	csi->last_eof = true;
499*9f257f50SLaurent Pinchart 	spin_unlock_irqrestore(&csi->irqlock, flags);
500*9f257f50SLaurent Pinchart 
501*9f257f50SLaurent Pinchart 	/*
502*9f257f50SLaurent Pinchart 	 * and then wait for interrupt handler to mark completion.
503*9f257f50SLaurent Pinchart 	 */
504*9f257f50SLaurent Pinchart 	timeout_jiffies = msecs_to_jiffies(IMX7_CSI_VIDEO_EOF_TIMEOUT);
505*9f257f50SLaurent Pinchart 	ret = wait_for_completion_timeout(&csi->last_eof_completion,
506*9f257f50SLaurent Pinchart 					  timeout_jiffies);
507*9f257f50SLaurent Pinchart 	if (ret == 0)
508*9f257f50SLaurent Pinchart 		v4l2_warn(&csi->sd, "wait last EOF timeout\n");
509*9f257f50SLaurent Pinchart 
510*9f257f50SLaurent Pinchart 	imx7_csi_hw_disable_irq(csi);
511*9f257f50SLaurent Pinchart }
512*9f257f50SLaurent Pinchart 
513*9f257f50SLaurent Pinchart static void imx7_csi_configure(struct imx7_csi *csi)
514*9f257f50SLaurent Pinchart {
515*9f257f50SLaurent Pinchart 	struct v4l2_pix_format *out_pix = &csi->vdev_fmt;
516*9f257f50SLaurent Pinchart 	int width = out_pix->width;
517*9f257f50SLaurent Pinchart 	u32 stride = 0;
518*9f257f50SLaurent Pinchart 	u32 cr3 = BIT_FRMCNT_RST;
519*9f257f50SLaurent Pinchart 	u32 cr1, cr18;
520*9f257f50SLaurent Pinchart 
521*9f257f50SLaurent Pinchart 	cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
522*9f257f50SLaurent Pinchart 
523*9f257f50SLaurent Pinchart 	cr18 &= ~(BIT_CSI_HW_ENABLE | BIT_MIPI_DATA_FORMAT_MASK |
524*9f257f50SLaurent Pinchart 		  BIT_DATA_FROM_MIPI | BIT_MIPI_DOUBLE_CMPNT |
525*9f257f50SLaurent Pinchart 		  BIT_BASEADDR_CHG_ERR_EN | BIT_BASEADDR_SWITCH_SEL |
526*9f257f50SLaurent Pinchart 		  BIT_BASEADDR_SWITCH_EN | BIT_DEINTERLACE_EN);
527*9f257f50SLaurent Pinchart 
528*9f257f50SLaurent Pinchart 	if (out_pix->field == V4L2_FIELD_INTERLACED) {
529*9f257f50SLaurent Pinchart 		cr18 |= BIT_DEINTERLACE_EN;
530*9f257f50SLaurent Pinchart 		stride = out_pix->width;
531*9f257f50SLaurent Pinchart 	}
532*9f257f50SLaurent Pinchart 
533*9f257f50SLaurent Pinchart 	if (!csi->is_csi2) {
534*9f257f50SLaurent Pinchart 		cr1 = BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | BIT_HSYNC_POL
535*9f257f50SLaurent Pinchart 		    | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN;
536*9f257f50SLaurent Pinchart 
537*9f257f50SLaurent Pinchart 		cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL |
538*9f257f50SLaurent Pinchart 			BIT_BASEADDR_CHG_ERR_EN;
539*9f257f50SLaurent Pinchart 
540*9f257f50SLaurent Pinchart 		if (out_pix->pixelformat == V4L2_PIX_FMT_UYVY ||
541*9f257f50SLaurent Pinchart 		    out_pix->pixelformat == V4L2_PIX_FMT_YUYV)
542*9f257f50SLaurent Pinchart 			width *= 2;
543*9f257f50SLaurent Pinchart 	} else {
544*9f257f50SLaurent Pinchart 		cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC
545*9f257f50SLaurent Pinchart 		    | BIT_MCLKDIV(1) | BIT_MCLKEN;
546*9f257f50SLaurent Pinchart 
547*9f257f50SLaurent Pinchart 		cr18 |= BIT_DATA_FROM_MIPI;
548*9f257f50SLaurent Pinchart 
549*9f257f50SLaurent Pinchart 		switch (csi->format_mbus[IMX7_CSI_PAD_SINK].code) {
550*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_Y8_1X8:
551*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SBGGR8_1X8:
552*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGBRG8_1X8:
553*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGRBG8_1X8:
554*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SRGGB8_1X8:
555*9f257f50SLaurent Pinchart 			cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
556*9f257f50SLaurent Pinchart 			break;
557*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_Y10_1X10:
558*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SBGGR10_1X10:
559*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGBRG10_1X10:
560*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGRBG10_1X10:
561*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SRGGB10_1X10:
562*9f257f50SLaurent Pinchart 			cr3 |= BIT_TWO_8BIT_SENSOR;
563*9f257f50SLaurent Pinchart 			cr18 |= BIT_MIPI_DATA_FORMAT_RAW10;
564*9f257f50SLaurent Pinchart 			break;
565*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_Y12_1X12:
566*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SBGGR12_1X12:
567*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGBRG12_1X12:
568*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGRBG12_1X12:
569*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SRGGB12_1X12:
570*9f257f50SLaurent Pinchart 			cr3 |= BIT_TWO_8BIT_SENSOR;
571*9f257f50SLaurent Pinchart 			cr18 |= BIT_MIPI_DATA_FORMAT_RAW12;
572*9f257f50SLaurent Pinchart 			break;
573*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_Y14_1X14:
574*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SBGGR14_1X14:
575*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGBRG14_1X14:
576*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SGRBG14_1X14:
577*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_SRGGB14_1X14:
578*9f257f50SLaurent Pinchart 			cr3 |= BIT_TWO_8BIT_SENSOR;
579*9f257f50SLaurent Pinchart 			cr18 |= BIT_MIPI_DATA_FORMAT_RAW14;
580*9f257f50SLaurent Pinchart 			break;
581*9f257f50SLaurent Pinchart 
582*9f257f50SLaurent Pinchart 		/*
583*9f257f50SLaurent Pinchart 		 * The CSI bridge has a 16-bit input bus. Depending on the
584*9f257f50SLaurent Pinchart 		 * connected source, data may be transmitted with 8 or 10 bits
585*9f257f50SLaurent Pinchart 		 * per clock sample (in bits [9:2] or [9:0] respectively) or
586*9f257f50SLaurent Pinchart 		 * with 16 bits per clock sample (in bits [15:0]). The data is
587*9f257f50SLaurent Pinchart 		 * then packed into a 32-bit FIFO (as shown in figure 13-11 of
588*9f257f50SLaurent Pinchart 		 * the i.MX8MM reference manual rev. 3).
589*9f257f50SLaurent Pinchart 		 *
590*9f257f50SLaurent Pinchart 		 * The data packing in a 32-bit FIFO input word is controlled by
591*9f257f50SLaurent Pinchart 		 * the CR3 TWO_8BIT_SENSOR field (also known as SENSOR_16BITS in
592*9f257f50SLaurent Pinchart 		 * the i.MX8MM reference manual). When set to 0, data packing
593*9f257f50SLaurent Pinchart 		 * groups four 8-bit input samples (bits [9:2]). When set to 1,
594*9f257f50SLaurent Pinchart 		 * data packing groups two 16-bit input samples (bits [15:0]).
595*9f257f50SLaurent Pinchart 		 *
596*9f257f50SLaurent Pinchart 		 * The register field CR18 MIPI_DOUBLE_CMPNT also needs to be
597*9f257f50SLaurent Pinchart 		 * configured according to the input format for YUV 4:2:2 data.
598*9f257f50SLaurent Pinchart 		 * The field controls the gasket between the CSI-2 receiver and
599*9f257f50SLaurent Pinchart 		 * the CSI bridge. On i.MX7 and i.MX8MM, the field must be set
600*9f257f50SLaurent Pinchart 		 * to 1 when the CSIS outputs 16-bit samples. On i.MX8MQ, the
601*9f257f50SLaurent Pinchart 		 * gasket ignores the MIPI_DOUBLE_CMPNT bit and YUV 4:2:2 always
602*9f257f50SLaurent Pinchart 		 * uses 16-bit samples. Setting MIPI_DOUBLE_CMPNT in that case
603*9f257f50SLaurent Pinchart 		 * has no effect, but doesn't cause any issue.
604*9f257f50SLaurent Pinchart 		 */
605*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_UYVY8_2X8:
606*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_YUYV8_2X8:
607*9f257f50SLaurent Pinchart 			cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
608*9f257f50SLaurent Pinchart 			break;
609*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_UYVY8_1X16:
610*9f257f50SLaurent Pinchart 		case MEDIA_BUS_FMT_YUYV8_1X16:
611*9f257f50SLaurent Pinchart 			cr3 |= BIT_TWO_8BIT_SENSOR;
612*9f257f50SLaurent Pinchart 			cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B |
613*9f257f50SLaurent Pinchart 				BIT_MIPI_DOUBLE_CMPNT;
614*9f257f50SLaurent Pinchart 			break;
615*9f257f50SLaurent Pinchart 		}
616*9f257f50SLaurent Pinchart 	}
617*9f257f50SLaurent Pinchart 
618*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
619*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, BIT_DMA_BURST_TYPE_RFF_INCR16, CSI_CSICR2);
620*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
621*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
622*9f257f50SLaurent Pinchart 
623*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, (width * out_pix->height) >> 2, CSI_CSIRXCNT);
624*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(width) |
625*9f257f50SLaurent Pinchart 			   BIT_IMAGE_HEIGHT(out_pix->height),
626*9f257f50SLaurent Pinchart 			   CSI_CSIIMAG_PARA);
627*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA);
628*9f257f50SLaurent Pinchart }
629*9f257f50SLaurent Pinchart 
630*9f257f50SLaurent Pinchart static int imx7_csi_init(struct imx7_csi *csi)
631*9f257f50SLaurent Pinchart {
632*9f257f50SLaurent Pinchart 	int ret;
633*9f257f50SLaurent Pinchart 
634*9f257f50SLaurent Pinchart 	ret = clk_prepare_enable(csi->mclk);
635*9f257f50SLaurent Pinchart 	if (ret < 0)
636*9f257f50SLaurent Pinchart 		return ret;
637*9f257f50SLaurent Pinchart 
638*9f257f50SLaurent Pinchart 	imx7_csi_configure(csi);
639*9f257f50SLaurent Pinchart 
640*9f257f50SLaurent Pinchart 	ret = imx7_csi_dma_setup(csi);
641*9f257f50SLaurent Pinchart 	if (ret < 0)
642*9f257f50SLaurent Pinchart 		return ret;
643*9f257f50SLaurent Pinchart 
644*9f257f50SLaurent Pinchart 	return 0;
645*9f257f50SLaurent Pinchart }
646*9f257f50SLaurent Pinchart 
647*9f257f50SLaurent Pinchart static void imx7_csi_deinit(struct imx7_csi *csi,
648*9f257f50SLaurent Pinchart 			    enum vb2_buffer_state return_status)
649*9f257f50SLaurent Pinchart {
650*9f257f50SLaurent Pinchart 	imx7_csi_dma_cleanup(csi, return_status);
651*9f257f50SLaurent Pinchart 	imx7_csi_init_default(csi);
652*9f257f50SLaurent Pinchart 	imx7_csi_dmareq_rff_disable(csi);
653*9f257f50SLaurent Pinchart 	clk_disable_unprepare(csi->mclk);
654*9f257f50SLaurent Pinchart }
655*9f257f50SLaurent Pinchart 
656*9f257f50SLaurent Pinchart static void imx7_csi_baseaddr_switch_on_second_frame(struct imx7_csi *csi)
657*9f257f50SLaurent Pinchart {
658*9f257f50SLaurent Pinchart 	u32 cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
659*9f257f50SLaurent Pinchart 
660*9f257f50SLaurent Pinchart 	cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL |
661*9f257f50SLaurent Pinchart 		BIT_BASEADDR_CHG_ERR_EN;
662*9f257f50SLaurent Pinchart 	cr18 |= BIT_MASK_OPTION_SECOND_FRAME;
663*9f257f50SLaurent Pinchart 	imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
664*9f257f50SLaurent Pinchart }
665*9f257f50SLaurent Pinchart 
666*9f257f50SLaurent Pinchart static void imx7_csi_enable(struct imx7_csi *csi)
667*9f257f50SLaurent Pinchart {
668*9f257f50SLaurent Pinchart 	/* Clear the Rx FIFO and reflash the DMA controller. */
669*9f257f50SLaurent Pinchart 	imx7_csi_rx_fifo_clear(csi);
670*9f257f50SLaurent Pinchart 	imx7_csi_dma_reflash(csi);
671*9f257f50SLaurent Pinchart 
672*9f257f50SLaurent Pinchart 	usleep_range(2000, 3000);
673*9f257f50SLaurent Pinchart 
674*9f257f50SLaurent Pinchart 	/* Clear and enable the interrupts. */
675*9f257f50SLaurent Pinchart 	imx7_csi_irq_clear(csi);
676*9f257f50SLaurent Pinchart 	imx7_csi_hw_enable_irq(csi);
677*9f257f50SLaurent Pinchart 
678*9f257f50SLaurent Pinchart 	/* Enable the RxFIFO DMA and the CSI. */
679*9f257f50SLaurent Pinchart 	imx7_csi_dmareq_rff_enable(csi);
680*9f257f50SLaurent Pinchart 	imx7_csi_hw_enable(csi);
681*9f257f50SLaurent Pinchart 
682*9f257f50SLaurent Pinchart 	if (csi->model == IMX7_CSI_IMX8MQ)
683*9f257f50SLaurent Pinchart 		imx7_csi_baseaddr_switch_on_second_frame(csi);
684*9f257f50SLaurent Pinchart }
685*9f257f50SLaurent Pinchart 
686*9f257f50SLaurent Pinchart static void imx7_csi_disable(struct imx7_csi *csi)
687*9f257f50SLaurent Pinchart {
688*9f257f50SLaurent Pinchart 	imx7_csi_dma_stop(csi);
689*9f257f50SLaurent Pinchart 
690*9f257f50SLaurent Pinchart 	imx7_csi_dmareq_rff_disable(csi);
691*9f257f50SLaurent Pinchart 
692*9f257f50SLaurent Pinchart 	imx7_csi_hw_disable_irq(csi);
693*9f257f50SLaurent Pinchart 
694*9f257f50SLaurent Pinchart 	imx7_csi_hw_disable(csi);
695*9f257f50SLaurent Pinchart }
696*9f257f50SLaurent Pinchart 
697*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
698*9f257f50SLaurent Pinchart  * Interrupt Handling
699*9f257f50SLaurent Pinchart  */
700*9f257f50SLaurent Pinchart 
701*9f257f50SLaurent Pinchart static void imx7_csi_error_recovery(struct imx7_csi *csi)
702*9f257f50SLaurent Pinchart {
703*9f257f50SLaurent Pinchart 	imx7_csi_hw_disable(csi);
704*9f257f50SLaurent Pinchart 
705*9f257f50SLaurent Pinchart 	imx7_csi_rx_fifo_clear(csi);
706*9f257f50SLaurent Pinchart 
707*9f257f50SLaurent Pinchart 	imx7_csi_dma_reflash(csi);
708*9f257f50SLaurent Pinchart 
709*9f257f50SLaurent Pinchart 	imx7_csi_hw_enable(csi);
710*9f257f50SLaurent Pinchart }
711*9f257f50SLaurent Pinchart 
712*9f257f50SLaurent Pinchart static void imx7_csi_vb2_buf_done(struct imx7_csi *csi)
713*9f257f50SLaurent Pinchart {
714*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *done, *next;
715*9f257f50SLaurent Pinchart 	struct vb2_buffer *vb;
716*9f257f50SLaurent Pinchart 	dma_addr_t dma_addr;
717*9f257f50SLaurent Pinchart 
718*9f257f50SLaurent Pinchart 	done = csi->active_vb2_buf[csi->buf_num];
719*9f257f50SLaurent Pinchart 	if (done) {
720*9f257f50SLaurent Pinchart 		done->vbuf.field = csi->vdev_fmt.field;
721*9f257f50SLaurent Pinchart 		done->vbuf.sequence = csi->frame_sequence;
722*9f257f50SLaurent Pinchart 		vb = &done->vbuf.vb2_buf;
723*9f257f50SLaurent Pinchart 		vb->timestamp = ktime_get_ns();
724*9f257f50SLaurent Pinchart 		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
725*9f257f50SLaurent Pinchart 	}
726*9f257f50SLaurent Pinchart 	csi->frame_sequence++;
727*9f257f50SLaurent Pinchart 
728*9f257f50SLaurent Pinchart 	/* get next queued buffer */
729*9f257f50SLaurent Pinchart 	next = imx7_csi_video_next_buf(csi);
730*9f257f50SLaurent Pinchart 	if (next) {
731*9f257f50SLaurent Pinchart 		dma_addr = vb2_dma_contig_plane_dma_addr(&next->vbuf.vb2_buf, 0);
732*9f257f50SLaurent Pinchart 		csi->active_vb2_buf[csi->buf_num] = next;
733*9f257f50SLaurent Pinchart 	} else {
734*9f257f50SLaurent Pinchart 		dma_addr = csi->underrun_buf.dma_addr;
735*9f257f50SLaurent Pinchart 		csi->active_vb2_buf[csi->buf_num] = NULL;
736*9f257f50SLaurent Pinchart 	}
737*9f257f50SLaurent Pinchart 
738*9f257f50SLaurent Pinchart 	imx7_csi_update_buf(csi, dma_addr, csi->buf_num);
739*9f257f50SLaurent Pinchart }
740*9f257f50SLaurent Pinchart 
741*9f257f50SLaurent Pinchart static irqreturn_t imx7_csi_irq_handler(int irq, void *data)
742*9f257f50SLaurent Pinchart {
743*9f257f50SLaurent Pinchart 	struct imx7_csi *csi =  data;
744*9f257f50SLaurent Pinchart 	u32 status;
745*9f257f50SLaurent Pinchart 
746*9f257f50SLaurent Pinchart 	spin_lock(&csi->irqlock);
747*9f257f50SLaurent Pinchart 
748*9f257f50SLaurent Pinchart 	status = imx7_csi_irq_clear(csi);
749*9f257f50SLaurent Pinchart 
750*9f257f50SLaurent Pinchart 	if (status & BIT_RFF_OR_INT) {
751*9f257f50SLaurent Pinchart 		dev_warn(csi->dev, "Rx fifo overflow\n");
752*9f257f50SLaurent Pinchart 		imx7_csi_error_recovery(csi);
753*9f257f50SLaurent Pinchart 	}
754*9f257f50SLaurent Pinchart 
755*9f257f50SLaurent Pinchart 	if (status & BIT_HRESP_ERR_INT) {
756*9f257f50SLaurent Pinchart 		dev_warn(csi->dev, "Hresponse error detected\n");
757*9f257f50SLaurent Pinchart 		imx7_csi_error_recovery(csi);
758*9f257f50SLaurent Pinchart 	}
759*9f257f50SLaurent Pinchart 
760*9f257f50SLaurent Pinchart 	if (status & BIT_ADDR_CH_ERR_INT) {
761*9f257f50SLaurent Pinchart 		imx7_csi_hw_disable(csi);
762*9f257f50SLaurent Pinchart 
763*9f257f50SLaurent Pinchart 		imx7_csi_dma_reflash(csi);
764*9f257f50SLaurent Pinchart 
765*9f257f50SLaurent Pinchart 		imx7_csi_hw_enable(csi);
766*9f257f50SLaurent Pinchart 	}
767*9f257f50SLaurent Pinchart 
768*9f257f50SLaurent Pinchart 	if ((status & BIT_DMA_TSF_DONE_FB1) &&
769*9f257f50SLaurent Pinchart 	    (status & BIT_DMA_TSF_DONE_FB2)) {
770*9f257f50SLaurent Pinchart 		/*
771*9f257f50SLaurent Pinchart 		 * For both FB1 and FB2 interrupter bits set case,
772*9f257f50SLaurent Pinchart 		 * CSI DMA is work in one of FB1 and FB2 buffer,
773*9f257f50SLaurent Pinchart 		 * but software can not know the state.
774*9f257f50SLaurent Pinchart 		 * Skip it to avoid base address updated
775*9f257f50SLaurent Pinchart 		 * when csi work in field0 and field1 will write to
776*9f257f50SLaurent Pinchart 		 * new base address.
777*9f257f50SLaurent Pinchart 		 */
778*9f257f50SLaurent Pinchart 	} else if (status & BIT_DMA_TSF_DONE_FB1) {
779*9f257f50SLaurent Pinchart 		csi->buf_num = 0;
780*9f257f50SLaurent Pinchart 	} else if (status & BIT_DMA_TSF_DONE_FB2) {
781*9f257f50SLaurent Pinchart 		csi->buf_num = 1;
782*9f257f50SLaurent Pinchart 	}
783*9f257f50SLaurent Pinchart 
784*9f257f50SLaurent Pinchart 	if ((status & BIT_DMA_TSF_DONE_FB1) ||
785*9f257f50SLaurent Pinchart 	    (status & BIT_DMA_TSF_DONE_FB2)) {
786*9f257f50SLaurent Pinchart 		imx7_csi_vb2_buf_done(csi);
787*9f257f50SLaurent Pinchart 
788*9f257f50SLaurent Pinchart 		if (csi->last_eof) {
789*9f257f50SLaurent Pinchart 			complete(&csi->last_eof_completion);
790*9f257f50SLaurent Pinchart 			csi->last_eof = false;
791*9f257f50SLaurent Pinchart 		}
792*9f257f50SLaurent Pinchart 	}
793*9f257f50SLaurent Pinchart 
794*9f257f50SLaurent Pinchart 	spin_unlock(&csi->irqlock);
795*9f257f50SLaurent Pinchart 
796*9f257f50SLaurent Pinchart 	return IRQ_HANDLED;
797*9f257f50SLaurent Pinchart }
798*9f257f50SLaurent Pinchart 
799*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
800*9f257f50SLaurent Pinchart  * Format Helpers
801*9f257f50SLaurent Pinchart  */
802*9f257f50SLaurent Pinchart 
803*9f257f50SLaurent Pinchart #define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0}
804*9f257f50SLaurent Pinchart 
805*9f257f50SLaurent Pinchart /*
806*9f257f50SLaurent Pinchart  * List of supported pixel formats for the subdevs. Keep V4L2_PIX_FMT_UYVY and
807*9f257f50SLaurent Pinchart  * MEDIA_BUS_FMT_UYVY8_2X8 first to match IMX7_CSI_DEF_PIX_FORMAT and
808*9f257f50SLaurent Pinchart  * IMX7_CSI_DEF_MBUS_CODE.
809*9f257f50SLaurent Pinchart  *
810*9f257f50SLaurent Pinchart  * TODO: Restrict the supported formats list based on the SoC integration.
811*9f257f50SLaurent Pinchart  *
812*9f257f50SLaurent Pinchart  * The CSI bridge can be configured to sample pixel components from the Rx queue
813*9f257f50SLaurent Pinchart  * in single (8bpp) or double (16bpp) component modes. Image format variants
814*9f257f50SLaurent Pinchart  * with different sample sizes (ie YUYV_2X8 vs YUYV_1X16) determine the pixel
815*9f257f50SLaurent Pinchart  * components sampling size per each clock cycle and their packing mode (see
816*9f257f50SLaurent Pinchart  * imx7_csi_configure() for details).
817*9f257f50SLaurent Pinchart  *
818*9f257f50SLaurent Pinchart  * As the CSI bridge can be interfaced with different IP blocks depending on the
819*9f257f50SLaurent Pinchart  * SoC model it is integrated on, the Rx queue sampling size should match the
820*9f257f50SLaurent Pinchart  * size of the samples transferred by the transmitting IP block. To avoid
821*9f257f50SLaurent Pinchart  * misconfigurations of the capture pipeline, the enumeration of the supported
822*9f257f50SLaurent Pinchart  * formats should be restricted to match the pixel source transmitting mode.
823*9f257f50SLaurent Pinchart  *
824*9f257f50SLaurent Pinchart  * Example: i.MX8MM SoC integrates the CSI bridge with the Samsung CSIS CSI-2
825*9f257f50SLaurent Pinchart  * receiver which operates in dual pixel sampling mode. The CSI bridge should
826*9f257f50SLaurent Pinchart  * only expose the 1X16 formats variant which instructs it to operate in dual
827*9f257f50SLaurent Pinchart  * pixel sampling mode. When the CSI bridge is instead integrated on an i.MX7,
828*9f257f50SLaurent Pinchart  * which supports both serial and parallel input, it should expose both
829*9f257f50SLaurent Pinchart  * variants.
830*9f257f50SLaurent Pinchart  *
831*9f257f50SLaurent Pinchart  * This currently only applies to YUYV formats, but other formats might need to
832*9f257f50SLaurent Pinchart  * be handled in the same way.
833*9f257f50SLaurent Pinchart  */
834*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt pixel_formats[] = {
835*9f257f50SLaurent Pinchart 	/*** YUV formats start here ***/
836*9f257f50SLaurent Pinchart 	{
837*9f257f50SLaurent Pinchart 		.fourcc	= V4L2_PIX_FMT_UYVY,
838*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(
839*9f257f50SLaurent Pinchart 			MEDIA_BUS_FMT_UYVY8_2X8,
840*9f257f50SLaurent Pinchart 			MEDIA_BUS_FMT_UYVY8_1X16
841*9f257f50SLaurent Pinchart 		),
842*9f257f50SLaurent Pinchart 		.yuv	= true,
843*9f257f50SLaurent Pinchart 		.bpp    = 16,
844*9f257f50SLaurent Pinchart 	}, {
845*9f257f50SLaurent Pinchart 		.fourcc	= V4L2_PIX_FMT_YUYV,
846*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(
847*9f257f50SLaurent Pinchart 			MEDIA_BUS_FMT_YUYV8_2X8,
848*9f257f50SLaurent Pinchart 			MEDIA_BUS_FMT_YUYV8_1X16
849*9f257f50SLaurent Pinchart 		),
850*9f257f50SLaurent Pinchart 		.yuv	= true,
851*9f257f50SLaurent Pinchart 		.bpp    = 16,
852*9f257f50SLaurent Pinchart 	},
853*9f257f50SLaurent Pinchart 	/*** raw bayer and grayscale formats start here ***/
854*9f257f50SLaurent Pinchart 	{
855*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SBGGR8,
856*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8),
857*9f257f50SLaurent Pinchart 		.bpp    = 8,
858*9f257f50SLaurent Pinchart 	}, {
859*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGBRG8,
860*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8),
861*9f257f50SLaurent Pinchart 		.bpp    = 8,
862*9f257f50SLaurent Pinchart 	}, {
863*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGRBG8,
864*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8),
865*9f257f50SLaurent Pinchart 		.bpp    = 8,
866*9f257f50SLaurent Pinchart 	}, {
867*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SRGGB8,
868*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8),
869*9f257f50SLaurent Pinchart 		.bpp    = 8,
870*9f257f50SLaurent Pinchart 	}, {
871*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SBGGR10,
872*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR10_1X10),
873*9f257f50SLaurent Pinchart 		.bpp    = 16,
874*9f257f50SLaurent Pinchart 	}, {
875*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGBRG10,
876*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG10_1X10),
877*9f257f50SLaurent Pinchart 		.bpp    = 16,
878*9f257f50SLaurent Pinchart 	}, {
879*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGRBG10,
880*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG10_1X10),
881*9f257f50SLaurent Pinchart 		.bpp    = 16,
882*9f257f50SLaurent Pinchart 	}, {
883*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SRGGB10,
884*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB10_1X10),
885*9f257f50SLaurent Pinchart 		.bpp    = 16,
886*9f257f50SLaurent Pinchart 	}, {
887*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SBGGR12,
888*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR12_1X12),
889*9f257f50SLaurent Pinchart 		.bpp    = 16,
890*9f257f50SLaurent Pinchart 	}, {
891*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGBRG12,
892*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG12_1X12),
893*9f257f50SLaurent Pinchart 		.bpp    = 16,
894*9f257f50SLaurent Pinchart 	}, {
895*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGRBG12,
896*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG12_1X12),
897*9f257f50SLaurent Pinchart 		.bpp    = 16,
898*9f257f50SLaurent Pinchart 	}, {
899*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SRGGB12,
900*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB12_1X12),
901*9f257f50SLaurent Pinchart 		.bpp    = 16,
902*9f257f50SLaurent Pinchart 	}, {
903*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SBGGR14,
904*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR14_1X14),
905*9f257f50SLaurent Pinchart 		.bpp    = 16,
906*9f257f50SLaurent Pinchart 	}, {
907*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGBRG14,
908*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG14_1X14),
909*9f257f50SLaurent Pinchart 		.bpp    = 16,
910*9f257f50SLaurent Pinchart 	}, {
911*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SGRBG14,
912*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG14_1X14),
913*9f257f50SLaurent Pinchart 		.bpp    = 16,
914*9f257f50SLaurent Pinchart 	}, {
915*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_SRGGB14,
916*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB14_1X14),
917*9f257f50SLaurent Pinchart 		.bpp    = 16,
918*9f257f50SLaurent Pinchart 	}, {
919*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_GREY,
920*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y8_1X8),
921*9f257f50SLaurent Pinchart 		.bpp    = 8,
922*9f257f50SLaurent Pinchart 	}, {
923*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_Y10,
924*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10),
925*9f257f50SLaurent Pinchart 		.bpp    = 16,
926*9f257f50SLaurent Pinchart 	}, {
927*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_Y12,
928*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12),
929*9f257f50SLaurent Pinchart 		.bpp    = 16,
930*9f257f50SLaurent Pinchart 	}, {
931*9f257f50SLaurent Pinchart 		.fourcc = V4L2_PIX_FMT_Y14,
932*9f257f50SLaurent Pinchart 		.codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y14_1X14),
933*9f257f50SLaurent Pinchart 		.bpp    = 16,
934*9f257f50SLaurent Pinchart 	},
935*9f257f50SLaurent Pinchart };
936*9f257f50SLaurent Pinchart 
937*9f257f50SLaurent Pinchart /*
938*9f257f50SLaurent Pinchart  * Search in the pixel_formats[] array for an entry with the given fourcc
939*9f257f50SLaurent Pinchart  * return it.
940*9f257f50SLaurent Pinchart  */
941*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt *imx7_csi_find_pixel_format(u32 fourcc)
942*9f257f50SLaurent Pinchart {
943*9f257f50SLaurent Pinchart 	unsigned int i;
944*9f257f50SLaurent Pinchart 
945*9f257f50SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
946*9f257f50SLaurent Pinchart 		const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
947*9f257f50SLaurent Pinchart 
948*9f257f50SLaurent Pinchart 		if (fmt->fourcc == fourcc)
949*9f257f50SLaurent Pinchart 			return fmt;
950*9f257f50SLaurent Pinchart 	}
951*9f257f50SLaurent Pinchart 
952*9f257f50SLaurent Pinchart 	return NULL;
953*9f257f50SLaurent Pinchart }
954*9f257f50SLaurent Pinchart 
955*9f257f50SLaurent Pinchart /*
956*9f257f50SLaurent Pinchart  * Search in the pixel_formats[] array for an entry with the given media
957*9f257f50SLaurent Pinchart  * bus code and return it.
958*9f257f50SLaurent Pinchart  */
959*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt *imx7_csi_find_mbus_format(u32 code)
960*9f257f50SLaurent Pinchart {
961*9f257f50SLaurent Pinchart 	unsigned int i;
962*9f257f50SLaurent Pinchart 
963*9f257f50SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
964*9f257f50SLaurent Pinchart 		const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
965*9f257f50SLaurent Pinchart 		unsigned int j;
966*9f257f50SLaurent Pinchart 
967*9f257f50SLaurent Pinchart 		if (!fmt->codes)
968*9f257f50SLaurent Pinchart 			continue;
969*9f257f50SLaurent Pinchart 
970*9f257f50SLaurent Pinchart 		for (j = 0; fmt->codes[j]; j++) {
971*9f257f50SLaurent Pinchart 			if (code == fmt->codes[j])
972*9f257f50SLaurent Pinchart 				return fmt;
973*9f257f50SLaurent Pinchart 		}
974*9f257f50SLaurent Pinchart 	}
975*9f257f50SLaurent Pinchart 
976*9f257f50SLaurent Pinchart 	return NULL;
977*9f257f50SLaurent Pinchart }
978*9f257f50SLaurent Pinchart 
979*9f257f50SLaurent Pinchart /*
980*9f257f50SLaurent Pinchart  * Enumerate entries in the pixel_formats[] array that match the
981*9f257f50SLaurent Pinchart  * requested search criteria. Return the media-bus code that matches
982*9f257f50SLaurent Pinchart  * the search criteria at the requested match index.
983*9f257f50SLaurent Pinchart  *
984*9f257f50SLaurent Pinchart  * @code: The returned media-bus code that matches the search criteria at
985*9f257f50SLaurent Pinchart  *        the requested match index.
986*9f257f50SLaurent Pinchart  * @index: The requested match index.
987*9f257f50SLaurent Pinchart  */
988*9f257f50SLaurent Pinchart static int imx7_csi_enum_mbus_formats(u32 *code, u32 index)
989*9f257f50SLaurent Pinchart {
990*9f257f50SLaurent Pinchart 	unsigned int i;
991*9f257f50SLaurent Pinchart 
992*9f257f50SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
993*9f257f50SLaurent Pinchart 		const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
994*9f257f50SLaurent Pinchart 		unsigned int j;
995*9f257f50SLaurent Pinchart 
996*9f257f50SLaurent Pinchart 		if (!fmt->codes)
997*9f257f50SLaurent Pinchart 			continue;
998*9f257f50SLaurent Pinchart 
999*9f257f50SLaurent Pinchart 		for (j = 0; fmt->codes[j]; j++) {
1000*9f257f50SLaurent Pinchart 			if (index == 0) {
1001*9f257f50SLaurent Pinchart 				*code = fmt->codes[j];
1002*9f257f50SLaurent Pinchart 				return 0;
1003*9f257f50SLaurent Pinchart 			}
1004*9f257f50SLaurent Pinchart 
1005*9f257f50SLaurent Pinchart 			index--;
1006*9f257f50SLaurent Pinchart 		}
1007*9f257f50SLaurent Pinchart 	}
1008*9f257f50SLaurent Pinchart 
1009*9f257f50SLaurent Pinchart 	return -EINVAL;
1010*9f257f50SLaurent Pinchart }
1011*9f257f50SLaurent Pinchart 
1012*9f257f50SLaurent Pinchart static int imx7_csi_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
1013*9f257f50SLaurent Pinchart 					const struct v4l2_mbus_framefmt *mbus,
1014*9f257f50SLaurent Pinchart 					const struct imx7_csi_pixfmt *cc)
1015*9f257f50SLaurent Pinchart {
1016*9f257f50SLaurent Pinchart 	u32 width;
1017*9f257f50SLaurent Pinchart 	u32 stride;
1018*9f257f50SLaurent Pinchart 
1019*9f257f50SLaurent Pinchart 	if (!cc) {
1020*9f257f50SLaurent Pinchart 		cc = imx7_csi_find_mbus_format(mbus->code);
1021*9f257f50SLaurent Pinchart 		if (!cc)
1022*9f257f50SLaurent Pinchart 			return -EINVAL;
1023*9f257f50SLaurent Pinchart 	}
1024*9f257f50SLaurent Pinchart 
1025*9f257f50SLaurent Pinchart 	/* Round up width for minimum burst size */
1026*9f257f50SLaurent Pinchart 	width = round_up(mbus->width, 8);
1027*9f257f50SLaurent Pinchart 
1028*9f257f50SLaurent Pinchart 	/* Round up stride for IDMAC line start address alignment */
1029*9f257f50SLaurent Pinchart 	stride = round_up((width * cc->bpp) >> 3, 8);
1030*9f257f50SLaurent Pinchart 
1031*9f257f50SLaurent Pinchart 	pix->width = width;
1032*9f257f50SLaurent Pinchart 	pix->height = mbus->height;
1033*9f257f50SLaurent Pinchart 	pix->pixelformat = cc->fourcc;
1034*9f257f50SLaurent Pinchart 	pix->colorspace = mbus->colorspace;
1035*9f257f50SLaurent Pinchart 	pix->xfer_func = mbus->xfer_func;
1036*9f257f50SLaurent Pinchart 	pix->ycbcr_enc = mbus->ycbcr_enc;
1037*9f257f50SLaurent Pinchart 	pix->quantization = mbus->quantization;
1038*9f257f50SLaurent Pinchart 	pix->field = mbus->field;
1039*9f257f50SLaurent Pinchart 	pix->bytesperline = stride;
1040*9f257f50SLaurent Pinchart 	pix->sizeimage = stride * pix->height;
1041*9f257f50SLaurent Pinchart 
1042*9f257f50SLaurent Pinchart 	return 0;
1043*9f257f50SLaurent Pinchart }
1044*9f257f50SLaurent Pinchart 
1045*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
1046*9f257f50SLaurent Pinchart  * Video Capture Device - IOCTLs
1047*9f257f50SLaurent Pinchart  */
1048*9f257f50SLaurent Pinchart 
1049*9f257f50SLaurent Pinchart static int imx7_csi_video_querycap(struct file *file, void *fh,
1050*9f257f50SLaurent Pinchart 				   struct v4l2_capability *cap)
1051*9f257f50SLaurent Pinchart {
1052*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = video_drvdata(file);
1053*9f257f50SLaurent Pinchart 
1054*9f257f50SLaurent Pinchart 	strscpy(cap->driver, IMX7_CSI_VIDEO_NAME, sizeof(cap->driver));
1055*9f257f50SLaurent Pinchart 	strscpy(cap->card, IMX7_CSI_VIDEO_NAME, sizeof(cap->card));
1056*9f257f50SLaurent Pinchart 	snprintf(cap->bus_info, sizeof(cap->bus_info),
1057*9f257f50SLaurent Pinchart 		 "platform:%s", dev_name(csi->dev));
1058*9f257f50SLaurent Pinchart 
1059*9f257f50SLaurent Pinchart 	return 0;
1060*9f257f50SLaurent Pinchart }
1061*9f257f50SLaurent Pinchart 
1062*9f257f50SLaurent Pinchart static int imx7_csi_video_enum_fmt_vid_cap(struct file *file, void *fh,
1063*9f257f50SLaurent Pinchart 					   struct v4l2_fmtdesc *f)
1064*9f257f50SLaurent Pinchart {
1065*9f257f50SLaurent Pinchart 	unsigned int index = f->index;
1066*9f257f50SLaurent Pinchart 	unsigned int i;
1067*9f257f50SLaurent Pinchart 
1068*9f257f50SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
1069*9f257f50SLaurent Pinchart 		const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
1070*9f257f50SLaurent Pinchart 
1071*9f257f50SLaurent Pinchart 		/*
1072*9f257f50SLaurent Pinchart 		 * If a media bus code is specified, only consider formats that
1073*9f257f50SLaurent Pinchart 		 * match it.
1074*9f257f50SLaurent Pinchart 		 */
1075*9f257f50SLaurent Pinchart 		if (f->mbus_code) {
1076*9f257f50SLaurent Pinchart 			unsigned int j;
1077*9f257f50SLaurent Pinchart 
1078*9f257f50SLaurent Pinchart 			if (!fmt->codes)
1079*9f257f50SLaurent Pinchart 				continue;
1080*9f257f50SLaurent Pinchart 
1081*9f257f50SLaurent Pinchart 			for (j = 0; fmt->codes[j]; j++) {
1082*9f257f50SLaurent Pinchart 				if (f->mbus_code == fmt->codes[j])
1083*9f257f50SLaurent Pinchart 					break;
1084*9f257f50SLaurent Pinchart 			}
1085*9f257f50SLaurent Pinchart 
1086*9f257f50SLaurent Pinchart 			if (!fmt->codes[j])
1087*9f257f50SLaurent Pinchart 				continue;
1088*9f257f50SLaurent Pinchart 		}
1089*9f257f50SLaurent Pinchart 
1090*9f257f50SLaurent Pinchart 		if (index == 0) {
1091*9f257f50SLaurent Pinchart 			f->pixelformat = fmt->fourcc;
1092*9f257f50SLaurent Pinchart 			return 0;
1093*9f257f50SLaurent Pinchart 		}
1094*9f257f50SLaurent Pinchart 
1095*9f257f50SLaurent Pinchart 		index--;
1096*9f257f50SLaurent Pinchart 	}
1097*9f257f50SLaurent Pinchart 
1098*9f257f50SLaurent Pinchart 	return -EINVAL;
1099*9f257f50SLaurent Pinchart }
1100*9f257f50SLaurent Pinchart 
1101*9f257f50SLaurent Pinchart static int imx7_csi_video_enum_framesizes(struct file *file, void *fh,
1102*9f257f50SLaurent Pinchart 					  struct v4l2_frmsizeenum *fsize)
1103*9f257f50SLaurent Pinchart {
1104*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1105*9f257f50SLaurent Pinchart 
1106*9f257f50SLaurent Pinchart 	if (fsize->index > 0)
1107*9f257f50SLaurent Pinchart 		return -EINVAL;
1108*9f257f50SLaurent Pinchart 
1109*9f257f50SLaurent Pinchart 	cc = imx7_csi_find_pixel_format(fsize->pixel_format);
1110*9f257f50SLaurent Pinchart 	if (!cc)
1111*9f257f50SLaurent Pinchart 		return -EINVAL;
1112*9f257f50SLaurent Pinchart 
1113*9f257f50SLaurent Pinchart 	/*
1114*9f257f50SLaurent Pinchart 	 * TODO: The constraints are hardware-specific and may depend on the
1115*9f257f50SLaurent Pinchart 	 * pixel format. This should come from the driver using
1116*9f257f50SLaurent Pinchart 	 * imx_media_capture.
1117*9f257f50SLaurent Pinchart 	 */
1118*9f257f50SLaurent Pinchart 	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
1119*9f257f50SLaurent Pinchart 	fsize->stepwise.min_width = 1;
1120*9f257f50SLaurent Pinchart 	fsize->stepwise.max_width = 65535;
1121*9f257f50SLaurent Pinchart 	fsize->stepwise.min_height = 1;
1122*9f257f50SLaurent Pinchart 	fsize->stepwise.max_height = 65535;
1123*9f257f50SLaurent Pinchart 	fsize->stepwise.step_width = 1;
1124*9f257f50SLaurent Pinchart 	fsize->stepwise.step_height = 1;
1125*9f257f50SLaurent Pinchart 
1126*9f257f50SLaurent Pinchart 	return 0;
1127*9f257f50SLaurent Pinchart }
1128*9f257f50SLaurent Pinchart 
1129*9f257f50SLaurent Pinchart static int imx7_csi_video_g_fmt_vid_cap(struct file *file, void *fh,
1130*9f257f50SLaurent Pinchart 					struct v4l2_format *f)
1131*9f257f50SLaurent Pinchart {
1132*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = video_drvdata(file);
1133*9f257f50SLaurent Pinchart 
1134*9f257f50SLaurent Pinchart 	f->fmt.pix = csi->vdev_fmt;
1135*9f257f50SLaurent Pinchart 
1136*9f257f50SLaurent Pinchart 	return 0;
1137*9f257f50SLaurent Pinchart }
1138*9f257f50SLaurent Pinchart 
1139*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt *
1140*9f257f50SLaurent Pinchart __imx7_csi_video_try_fmt(struct v4l2_pix_format *pixfmt,
1141*9f257f50SLaurent Pinchart 			 struct v4l2_rect *compose)
1142*9f257f50SLaurent Pinchart {
1143*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt fmt_src;
1144*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1145*9f257f50SLaurent Pinchart 
1146*9f257f50SLaurent Pinchart 	/*
1147*9f257f50SLaurent Pinchart 	 * Find the pixel format, default to the first supported format if not
1148*9f257f50SLaurent Pinchart 	 * found.
1149*9f257f50SLaurent Pinchart 	 */
1150*9f257f50SLaurent Pinchart 	cc = imx7_csi_find_pixel_format(pixfmt->pixelformat);
1151*9f257f50SLaurent Pinchart 	if (!cc) {
1152*9f257f50SLaurent Pinchart 		pixfmt->pixelformat = IMX7_CSI_DEF_PIX_FORMAT;
1153*9f257f50SLaurent Pinchart 		cc = imx7_csi_find_pixel_format(pixfmt->pixelformat);
1154*9f257f50SLaurent Pinchart 	}
1155*9f257f50SLaurent Pinchart 
1156*9f257f50SLaurent Pinchart 	/* Allow IDMAC interweave but enforce field order from source. */
1157*9f257f50SLaurent Pinchart 	if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) {
1158*9f257f50SLaurent Pinchart 		switch (pixfmt->field) {
1159*9f257f50SLaurent Pinchart 		case V4L2_FIELD_SEQ_TB:
1160*9f257f50SLaurent Pinchart 			pixfmt->field = V4L2_FIELD_INTERLACED_TB;
1161*9f257f50SLaurent Pinchart 			break;
1162*9f257f50SLaurent Pinchart 		case V4L2_FIELD_SEQ_BT:
1163*9f257f50SLaurent Pinchart 			pixfmt->field = V4L2_FIELD_INTERLACED_BT;
1164*9f257f50SLaurent Pinchart 			break;
1165*9f257f50SLaurent Pinchart 		default:
1166*9f257f50SLaurent Pinchart 			break;
1167*9f257f50SLaurent Pinchart 		}
1168*9f257f50SLaurent Pinchart 	}
1169*9f257f50SLaurent Pinchart 
1170*9f257f50SLaurent Pinchart 	v4l2_fill_mbus_format(&fmt_src, pixfmt, 0);
1171*9f257f50SLaurent Pinchart 	imx7_csi_mbus_fmt_to_pix_fmt(pixfmt, &fmt_src, cc);
1172*9f257f50SLaurent Pinchart 
1173*9f257f50SLaurent Pinchart 	if (compose) {
1174*9f257f50SLaurent Pinchart 		compose->width = fmt_src.width;
1175*9f257f50SLaurent Pinchart 		compose->height = fmt_src.height;
1176*9f257f50SLaurent Pinchart 	}
1177*9f257f50SLaurent Pinchart 
1178*9f257f50SLaurent Pinchart 	return cc;
1179*9f257f50SLaurent Pinchart }
1180*9f257f50SLaurent Pinchart 
1181*9f257f50SLaurent Pinchart static int imx7_csi_video_try_fmt_vid_cap(struct file *file, void *fh,
1182*9f257f50SLaurent Pinchart 					  struct v4l2_format *f)
1183*9f257f50SLaurent Pinchart {
1184*9f257f50SLaurent Pinchart 	__imx7_csi_video_try_fmt(&f->fmt.pix, NULL);
1185*9f257f50SLaurent Pinchart 	return 0;
1186*9f257f50SLaurent Pinchart }
1187*9f257f50SLaurent Pinchart 
1188*9f257f50SLaurent Pinchart static int imx7_csi_video_s_fmt_vid_cap(struct file *file, void *fh,
1189*9f257f50SLaurent Pinchart 					struct v4l2_format *f)
1190*9f257f50SLaurent Pinchart {
1191*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = video_drvdata(file);
1192*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1193*9f257f50SLaurent Pinchart 
1194*9f257f50SLaurent Pinchart 	if (vb2_is_busy(&csi->q)) {
1195*9f257f50SLaurent Pinchart 		dev_err(csi->dev, "%s queue busy\n", __func__);
1196*9f257f50SLaurent Pinchart 		return -EBUSY;
1197*9f257f50SLaurent Pinchart 	}
1198*9f257f50SLaurent Pinchart 
1199*9f257f50SLaurent Pinchart 	cc = __imx7_csi_video_try_fmt(&f->fmt.pix, &csi->vdev_compose);
1200*9f257f50SLaurent Pinchart 
1201*9f257f50SLaurent Pinchart 	csi->vdev_cc = cc;
1202*9f257f50SLaurent Pinchart 	csi->vdev_fmt = f->fmt.pix;
1203*9f257f50SLaurent Pinchart 
1204*9f257f50SLaurent Pinchart 	return 0;
1205*9f257f50SLaurent Pinchart }
1206*9f257f50SLaurent Pinchart 
1207*9f257f50SLaurent Pinchart static int imx7_csi_video_g_selection(struct file *file, void *fh,
1208*9f257f50SLaurent Pinchart 				      struct v4l2_selection *s)
1209*9f257f50SLaurent Pinchart {
1210*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = video_drvdata(file);
1211*9f257f50SLaurent Pinchart 
1212*9f257f50SLaurent Pinchart 	switch (s->target) {
1213*9f257f50SLaurent Pinchart 	case V4L2_SEL_TGT_COMPOSE:
1214*9f257f50SLaurent Pinchart 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
1215*9f257f50SLaurent Pinchart 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
1216*9f257f50SLaurent Pinchart 		/* The compose rectangle is fixed to the source format. */
1217*9f257f50SLaurent Pinchart 		s->r = csi->vdev_compose;
1218*9f257f50SLaurent Pinchart 		break;
1219*9f257f50SLaurent Pinchart 	case V4L2_SEL_TGT_COMPOSE_PADDED:
1220*9f257f50SLaurent Pinchart 		/*
1221*9f257f50SLaurent Pinchart 		 * The hardware writes with a configurable but fixed DMA burst
1222*9f257f50SLaurent Pinchart 		 * size. If the source format width is not burst size aligned,
1223*9f257f50SLaurent Pinchart 		 * the written frame contains padding to the right.
1224*9f257f50SLaurent Pinchart 		 */
1225*9f257f50SLaurent Pinchart 		s->r.left = 0;
1226*9f257f50SLaurent Pinchart 		s->r.top = 0;
1227*9f257f50SLaurent Pinchart 		s->r.width = csi->vdev_fmt.width;
1228*9f257f50SLaurent Pinchart 		s->r.height = csi->vdev_fmt.height;
1229*9f257f50SLaurent Pinchart 		break;
1230*9f257f50SLaurent Pinchart 	default:
1231*9f257f50SLaurent Pinchart 		return -EINVAL;
1232*9f257f50SLaurent Pinchart 	}
1233*9f257f50SLaurent Pinchart 
1234*9f257f50SLaurent Pinchart 	return 0;
1235*9f257f50SLaurent Pinchart }
1236*9f257f50SLaurent Pinchart 
1237*9f257f50SLaurent Pinchart static const struct v4l2_ioctl_ops imx7_csi_video_ioctl_ops = {
1238*9f257f50SLaurent Pinchart 	.vidioc_querycap		= imx7_csi_video_querycap,
1239*9f257f50SLaurent Pinchart 
1240*9f257f50SLaurent Pinchart 	.vidioc_enum_fmt_vid_cap	= imx7_csi_video_enum_fmt_vid_cap,
1241*9f257f50SLaurent Pinchart 	.vidioc_enum_framesizes		= imx7_csi_video_enum_framesizes,
1242*9f257f50SLaurent Pinchart 
1243*9f257f50SLaurent Pinchart 	.vidioc_g_fmt_vid_cap		= imx7_csi_video_g_fmt_vid_cap,
1244*9f257f50SLaurent Pinchart 	.vidioc_try_fmt_vid_cap		= imx7_csi_video_try_fmt_vid_cap,
1245*9f257f50SLaurent Pinchart 	.vidioc_s_fmt_vid_cap		= imx7_csi_video_s_fmt_vid_cap,
1246*9f257f50SLaurent Pinchart 
1247*9f257f50SLaurent Pinchart 	.vidioc_g_selection		= imx7_csi_video_g_selection,
1248*9f257f50SLaurent Pinchart 
1249*9f257f50SLaurent Pinchart 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
1250*9f257f50SLaurent Pinchart 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
1251*9f257f50SLaurent Pinchart 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
1252*9f257f50SLaurent Pinchart 	.vidioc_querybuf		= vb2_ioctl_querybuf,
1253*9f257f50SLaurent Pinchart 	.vidioc_qbuf			= vb2_ioctl_qbuf,
1254*9f257f50SLaurent Pinchart 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
1255*9f257f50SLaurent Pinchart 	.vidioc_expbuf			= vb2_ioctl_expbuf,
1256*9f257f50SLaurent Pinchart 	.vidioc_streamon		= vb2_ioctl_streamon,
1257*9f257f50SLaurent Pinchart 	.vidioc_streamoff		= vb2_ioctl_streamoff,
1258*9f257f50SLaurent Pinchart };
1259*9f257f50SLaurent Pinchart 
1260*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
1261*9f257f50SLaurent Pinchart  * Video Capture Device - Queue Operations
1262*9f257f50SLaurent Pinchart  */
1263*9f257f50SLaurent Pinchart 
1264*9f257f50SLaurent Pinchart static int imx7_csi_video_queue_setup(struct vb2_queue *vq,
1265*9f257f50SLaurent Pinchart 				      unsigned int *nbuffers,
1266*9f257f50SLaurent Pinchart 				      unsigned int *nplanes,
1267*9f257f50SLaurent Pinchart 				      unsigned int sizes[],
1268*9f257f50SLaurent Pinchart 				      struct device *alloc_devs[])
1269*9f257f50SLaurent Pinchart {
1270*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = vb2_get_drv_priv(vq);
1271*9f257f50SLaurent Pinchart 	struct v4l2_pix_format *pix = &csi->vdev_fmt;
1272*9f257f50SLaurent Pinchart 	unsigned int count = *nbuffers;
1273*9f257f50SLaurent Pinchart 
1274*9f257f50SLaurent Pinchart 	if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1275*9f257f50SLaurent Pinchart 		return -EINVAL;
1276*9f257f50SLaurent Pinchart 
1277*9f257f50SLaurent Pinchart 	if (*nplanes) {
1278*9f257f50SLaurent Pinchart 		if (*nplanes != 1 || sizes[0] < pix->sizeimage)
1279*9f257f50SLaurent Pinchart 			return -EINVAL;
1280*9f257f50SLaurent Pinchart 		count += vq->num_buffers;
1281*9f257f50SLaurent Pinchart 	}
1282*9f257f50SLaurent Pinchart 
1283*9f257f50SLaurent Pinchart 	count = min_t(__u32, IMX7_CSI_VIDEO_MEM_LIMIT / pix->sizeimage, count);
1284*9f257f50SLaurent Pinchart 
1285*9f257f50SLaurent Pinchart 	if (*nplanes)
1286*9f257f50SLaurent Pinchart 		*nbuffers = (count < vq->num_buffers) ? 0 :
1287*9f257f50SLaurent Pinchart 			count - vq->num_buffers;
1288*9f257f50SLaurent Pinchart 	else
1289*9f257f50SLaurent Pinchart 		*nbuffers = count;
1290*9f257f50SLaurent Pinchart 
1291*9f257f50SLaurent Pinchart 	*nplanes = 1;
1292*9f257f50SLaurent Pinchart 	sizes[0] = pix->sizeimage;
1293*9f257f50SLaurent Pinchart 
1294*9f257f50SLaurent Pinchart 	return 0;
1295*9f257f50SLaurent Pinchart }
1296*9f257f50SLaurent Pinchart 
1297*9f257f50SLaurent Pinchart static int imx7_csi_video_buf_init(struct vb2_buffer *vb)
1298*9f257f50SLaurent Pinchart {
1299*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb);
1300*9f257f50SLaurent Pinchart 
1301*9f257f50SLaurent Pinchart 	INIT_LIST_HEAD(&buf->list);
1302*9f257f50SLaurent Pinchart 
1303*9f257f50SLaurent Pinchart 	return 0;
1304*9f257f50SLaurent Pinchart }
1305*9f257f50SLaurent Pinchart 
1306*9f257f50SLaurent Pinchart static int imx7_csi_video_buf_prepare(struct vb2_buffer *vb)
1307*9f257f50SLaurent Pinchart {
1308*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
1309*9f257f50SLaurent Pinchart 	struct v4l2_pix_format *pix = &csi->vdev_fmt;
1310*9f257f50SLaurent Pinchart 
1311*9f257f50SLaurent Pinchart 	if (vb2_plane_size(vb, 0) < pix->sizeimage) {
1312*9f257f50SLaurent Pinchart 		dev_err(csi->dev,
1313*9f257f50SLaurent Pinchart 			"data will not fit into plane (%lu < %lu)\n",
1314*9f257f50SLaurent Pinchart 			vb2_plane_size(vb, 0), (long)pix->sizeimage);
1315*9f257f50SLaurent Pinchart 		return -EINVAL;
1316*9f257f50SLaurent Pinchart 	}
1317*9f257f50SLaurent Pinchart 
1318*9f257f50SLaurent Pinchart 	vb2_set_plane_payload(vb, 0, pix->sizeimage);
1319*9f257f50SLaurent Pinchart 
1320*9f257f50SLaurent Pinchart 	return 0;
1321*9f257f50SLaurent Pinchart }
1322*9f257f50SLaurent Pinchart 
1323*9f257f50SLaurent Pinchart static bool imx7_csi_fast_track_buffer(struct imx7_csi *csi,
1324*9f257f50SLaurent Pinchart 				       struct imx7_csi_vb2_buffer *buf)
1325*9f257f50SLaurent Pinchart {
1326*9f257f50SLaurent Pinchart 	unsigned long flags;
1327*9f257f50SLaurent Pinchart 	dma_addr_t dma_addr;
1328*9f257f50SLaurent Pinchart 	int buf_num;
1329*9f257f50SLaurent Pinchart 	u32 isr;
1330*9f257f50SLaurent Pinchart 
1331*9f257f50SLaurent Pinchart 	if (!csi->is_streaming)
1332*9f257f50SLaurent Pinchart 		return false;
1333*9f257f50SLaurent Pinchart 
1334*9f257f50SLaurent Pinchart 	dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vbuf.vb2_buf, 0);
1335*9f257f50SLaurent Pinchart 
1336*9f257f50SLaurent Pinchart 	/*
1337*9f257f50SLaurent Pinchart 	 * buf_num holds the framebuffer ID of the most recently (*not* the
1338*9f257f50SLaurent Pinchart 	 * next anticipated) triggered interrupt. Without loss of generality,
1339*9f257f50SLaurent Pinchart 	 * if buf_num is 0, the hardware is capturing to FB2. If FB1 has been
1340*9f257f50SLaurent Pinchart 	 * programmed with a dummy buffer (as indicated by active_vb2_buf[0]
1341*9f257f50SLaurent Pinchart 	 * being NULL), then we can fast-track the new buffer by programming
1342*9f257f50SLaurent Pinchart 	 * its address in FB1 before the hardware completes FB2, instead of
1343*9f257f50SLaurent Pinchart 	 * adding it to the buffer queue and incurring a delay of one
1344*9f257f50SLaurent Pinchart 	 * additional frame.
1345*9f257f50SLaurent Pinchart 	 *
1346*9f257f50SLaurent Pinchart 	 * The irqlock prevents races with the interrupt handler that updates
1347*9f257f50SLaurent Pinchart 	 * buf_num when it programs the next buffer, but we can still race with
1348*9f257f50SLaurent Pinchart 	 * the hardware if we program the buffer in FB1 just after the hardware
1349*9f257f50SLaurent Pinchart 	 * completes FB2 and switches to FB1 and before buf_num can be updated
1350*9f257f50SLaurent Pinchart 	 * by the interrupt handler for FB2.  The fast-tracked buffer would
1351*9f257f50SLaurent Pinchart 	 * then be ignored by the hardware while the driver would think it has
1352*9f257f50SLaurent Pinchart 	 * successfully been processed.
1353*9f257f50SLaurent Pinchart 	 *
1354*9f257f50SLaurent Pinchart 	 * To avoid this problem, if we can't avoid the race, we can detect
1355*9f257f50SLaurent Pinchart 	 * that we have lost it by checking, after programming the buffer in
1356*9f257f50SLaurent Pinchart 	 * FB1, if the interrupt flag indicating completion of FB2 has been
1357*9f257f50SLaurent Pinchart 	 * raised. If that is not the case, fast-tracking succeeded, and we can
1358*9f257f50SLaurent Pinchart 	 * update active_vb2_buf[0]. Otherwise, we may or may not have lost the
1359*9f257f50SLaurent Pinchart 	 * race (as the interrupt flag may have been raised just after
1360*9f257f50SLaurent Pinchart 	 * programming FB1 and before we read the interrupt status register),
1361*9f257f50SLaurent Pinchart 	 * and we need to assume the worst case of a race loss and queue the
1362*9f257f50SLaurent Pinchart 	 * buffer through the slow path.
1363*9f257f50SLaurent Pinchart 	 */
1364*9f257f50SLaurent Pinchart 
1365*9f257f50SLaurent Pinchart 	spin_lock_irqsave(&csi->irqlock, flags);
1366*9f257f50SLaurent Pinchart 
1367*9f257f50SLaurent Pinchart 	buf_num = csi->buf_num;
1368*9f257f50SLaurent Pinchart 	if (csi->active_vb2_buf[buf_num]) {
1369*9f257f50SLaurent Pinchart 		spin_unlock_irqrestore(&csi->irqlock, flags);
1370*9f257f50SLaurent Pinchart 		return false;
1371*9f257f50SLaurent Pinchart 	}
1372*9f257f50SLaurent Pinchart 
1373*9f257f50SLaurent Pinchart 	imx7_csi_update_buf(csi, dma_addr, buf_num);
1374*9f257f50SLaurent Pinchart 
1375*9f257f50SLaurent Pinchart 	isr = imx7_csi_reg_read(csi, CSI_CSISR);
1376*9f257f50SLaurent Pinchart 	if (isr & (buf_num ? BIT_DMA_TSF_DONE_FB1 : BIT_DMA_TSF_DONE_FB2)) {
1377*9f257f50SLaurent Pinchart 		/*
1378*9f257f50SLaurent Pinchart 		 * The interrupt for the /other/ FB just came (the isr hasn't
1379*9f257f50SLaurent Pinchart 		 * run yet though, because we have the lock here); we can't be
1380*9f257f50SLaurent Pinchart 		 * sure we've programmed buf_num FB in time, so queue the buffer
1381*9f257f50SLaurent Pinchart 		 * to the buffer queue normally. No need to undo writing the FB
1382*9f257f50SLaurent Pinchart 		 * register, since we won't return it as active_vb2_buf is NULL,
1383*9f257f50SLaurent Pinchart 		 * so it's okay to potentially write it to both FB1 and FB2;
1384*9f257f50SLaurent Pinchart 		 * only the one where it was queued normally will be returned.
1385*9f257f50SLaurent Pinchart 		 */
1386*9f257f50SLaurent Pinchart 		spin_unlock_irqrestore(&csi->irqlock, flags);
1387*9f257f50SLaurent Pinchart 		return false;
1388*9f257f50SLaurent Pinchart 	}
1389*9f257f50SLaurent Pinchart 
1390*9f257f50SLaurent Pinchart 	csi->active_vb2_buf[buf_num] = buf;
1391*9f257f50SLaurent Pinchart 
1392*9f257f50SLaurent Pinchart 	spin_unlock_irqrestore(&csi->irqlock, flags);
1393*9f257f50SLaurent Pinchart 	return true;
1394*9f257f50SLaurent Pinchart }
1395*9f257f50SLaurent Pinchart 
1396*9f257f50SLaurent Pinchart static void imx7_csi_video_buf_queue(struct vb2_buffer *vb)
1397*9f257f50SLaurent Pinchart {
1398*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
1399*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb);
1400*9f257f50SLaurent Pinchart 	unsigned long flags;
1401*9f257f50SLaurent Pinchart 
1402*9f257f50SLaurent Pinchart 	if (imx7_csi_fast_track_buffer(csi, buf))
1403*9f257f50SLaurent Pinchart 		return;
1404*9f257f50SLaurent Pinchart 
1405*9f257f50SLaurent Pinchart 	spin_lock_irqsave(&csi->q_lock, flags);
1406*9f257f50SLaurent Pinchart 
1407*9f257f50SLaurent Pinchart 	list_add_tail(&buf->list, &csi->ready_q);
1408*9f257f50SLaurent Pinchart 
1409*9f257f50SLaurent Pinchart 	spin_unlock_irqrestore(&csi->q_lock, flags);
1410*9f257f50SLaurent Pinchart }
1411*9f257f50SLaurent Pinchart 
1412*9f257f50SLaurent Pinchart static int imx7_csi_video_validate_fmt(struct imx7_csi *csi)
1413*9f257f50SLaurent Pinchart {
1414*9f257f50SLaurent Pinchart 	struct v4l2_subdev_format fmt_src;
1415*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1416*9f257f50SLaurent Pinchart 	int ret;
1417*9f257f50SLaurent Pinchart 
1418*9f257f50SLaurent Pinchart 	/* Retrieve the media bus format on the source subdev. */
1419*9f257f50SLaurent Pinchart 	fmt_src.pad = IMX7_CSI_PAD_SRC;
1420*9f257f50SLaurent Pinchart 	fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
1421*9f257f50SLaurent Pinchart 	ret = v4l2_subdev_call(&csi->sd, pad, get_fmt, NULL, &fmt_src);
1422*9f257f50SLaurent Pinchart 	if (ret)
1423*9f257f50SLaurent Pinchart 		return ret;
1424*9f257f50SLaurent Pinchart 
1425*9f257f50SLaurent Pinchart 	/*
1426*9f257f50SLaurent Pinchart 	 * Verify that the media bus size matches the size set on the video
1427*9f257f50SLaurent Pinchart 	 * node. It is sufficient to check the compose rectangle size without
1428*9f257f50SLaurent Pinchart 	 * checking the rounded size from pix_fmt, as the rounded size is
1429*9f257f50SLaurent Pinchart 	 * derived directly from the compose rectangle size, and will thus
1430*9f257f50SLaurent Pinchart 	 * always match if the compose rectangle matches.
1431*9f257f50SLaurent Pinchart 	 */
1432*9f257f50SLaurent Pinchart 	if (csi->vdev_compose.width != fmt_src.format.width ||
1433*9f257f50SLaurent Pinchart 	    csi->vdev_compose.height != fmt_src.format.height)
1434*9f257f50SLaurent Pinchart 		return -EPIPE;
1435*9f257f50SLaurent Pinchart 
1436*9f257f50SLaurent Pinchart 	/*
1437*9f257f50SLaurent Pinchart 	 * Verify that the media bus code is compatible with the pixel format
1438*9f257f50SLaurent Pinchart 	 * set on the video node.
1439*9f257f50SLaurent Pinchart 	 */
1440*9f257f50SLaurent Pinchart 	cc = imx7_csi_find_mbus_format(fmt_src.format.code);
1441*9f257f50SLaurent Pinchart 	if (!cc || csi->vdev_cc->yuv != cc->yuv)
1442*9f257f50SLaurent Pinchart 		return -EPIPE;
1443*9f257f50SLaurent Pinchart 
1444*9f257f50SLaurent Pinchart 	return 0;
1445*9f257f50SLaurent Pinchart }
1446*9f257f50SLaurent Pinchart 
1447*9f257f50SLaurent Pinchart static int imx7_csi_video_start_streaming(struct vb2_queue *vq,
1448*9f257f50SLaurent Pinchart 					  unsigned int count)
1449*9f257f50SLaurent Pinchart {
1450*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = vb2_get_drv_priv(vq);
1451*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *buf, *tmp;
1452*9f257f50SLaurent Pinchart 	unsigned long flags;
1453*9f257f50SLaurent Pinchart 	int ret;
1454*9f257f50SLaurent Pinchart 
1455*9f257f50SLaurent Pinchart 	ret = imx7_csi_video_validate_fmt(csi);
1456*9f257f50SLaurent Pinchart 	if (ret) {
1457*9f257f50SLaurent Pinchart 		dev_err(csi->dev, "capture format not valid\n");
1458*9f257f50SLaurent Pinchart 		goto err_buffers;
1459*9f257f50SLaurent Pinchart 	}
1460*9f257f50SLaurent Pinchart 
1461*9f257f50SLaurent Pinchart 	mutex_lock(&csi->mdev.graph_mutex);
1462*9f257f50SLaurent Pinchart 
1463*9f257f50SLaurent Pinchart 	ret = __video_device_pipeline_start(csi->vdev, &csi->pipe);
1464*9f257f50SLaurent Pinchart 	if (ret)
1465*9f257f50SLaurent Pinchart 		goto err_unlock;
1466*9f257f50SLaurent Pinchart 
1467*9f257f50SLaurent Pinchart 	ret = v4l2_subdev_call(&csi->sd, video, s_stream, 1);
1468*9f257f50SLaurent Pinchart 	if (ret)
1469*9f257f50SLaurent Pinchart 		goto err_stop;
1470*9f257f50SLaurent Pinchart 
1471*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->mdev.graph_mutex);
1472*9f257f50SLaurent Pinchart 
1473*9f257f50SLaurent Pinchart 	return 0;
1474*9f257f50SLaurent Pinchart 
1475*9f257f50SLaurent Pinchart err_stop:
1476*9f257f50SLaurent Pinchart 	__video_device_pipeline_stop(csi->vdev);
1477*9f257f50SLaurent Pinchart err_unlock:
1478*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->mdev.graph_mutex);
1479*9f257f50SLaurent Pinchart 	dev_err(csi->dev, "pipeline start failed with %d\n", ret);
1480*9f257f50SLaurent Pinchart err_buffers:
1481*9f257f50SLaurent Pinchart 	spin_lock_irqsave(&csi->q_lock, flags);
1482*9f257f50SLaurent Pinchart 	list_for_each_entry_safe(buf, tmp, &csi->ready_q, list) {
1483*9f257f50SLaurent Pinchart 		list_del(&buf->list);
1484*9f257f50SLaurent Pinchart 		vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED);
1485*9f257f50SLaurent Pinchart 	}
1486*9f257f50SLaurent Pinchart 	spin_unlock_irqrestore(&csi->q_lock, flags);
1487*9f257f50SLaurent Pinchart 	return ret;
1488*9f257f50SLaurent Pinchart }
1489*9f257f50SLaurent Pinchart 
1490*9f257f50SLaurent Pinchart static void imx7_csi_video_stop_streaming(struct vb2_queue *vq)
1491*9f257f50SLaurent Pinchart {
1492*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = vb2_get_drv_priv(vq);
1493*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *frame;
1494*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *tmp;
1495*9f257f50SLaurent Pinchart 	unsigned long flags;
1496*9f257f50SLaurent Pinchart 
1497*9f257f50SLaurent Pinchart 	mutex_lock(&csi->mdev.graph_mutex);
1498*9f257f50SLaurent Pinchart 	v4l2_subdev_call(&csi->sd, video, s_stream, 0);
1499*9f257f50SLaurent Pinchart 	__video_device_pipeline_stop(csi->vdev);
1500*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->mdev.graph_mutex);
1501*9f257f50SLaurent Pinchart 
1502*9f257f50SLaurent Pinchart 	/* release all active buffers */
1503*9f257f50SLaurent Pinchart 	spin_lock_irqsave(&csi->q_lock, flags);
1504*9f257f50SLaurent Pinchart 	list_for_each_entry_safe(frame, tmp, &csi->ready_q, list) {
1505*9f257f50SLaurent Pinchart 		list_del(&frame->list);
1506*9f257f50SLaurent Pinchart 		vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
1507*9f257f50SLaurent Pinchart 	}
1508*9f257f50SLaurent Pinchart 	spin_unlock_irqrestore(&csi->q_lock, flags);
1509*9f257f50SLaurent Pinchart }
1510*9f257f50SLaurent Pinchart 
1511*9f257f50SLaurent Pinchart static const struct vb2_ops imx7_csi_video_qops = {
1512*9f257f50SLaurent Pinchart 	.queue_setup	 = imx7_csi_video_queue_setup,
1513*9f257f50SLaurent Pinchart 	.buf_init        = imx7_csi_video_buf_init,
1514*9f257f50SLaurent Pinchart 	.buf_prepare	 = imx7_csi_video_buf_prepare,
1515*9f257f50SLaurent Pinchart 	.buf_queue	 = imx7_csi_video_buf_queue,
1516*9f257f50SLaurent Pinchart 	.wait_prepare	 = vb2_ops_wait_prepare,
1517*9f257f50SLaurent Pinchart 	.wait_finish	 = vb2_ops_wait_finish,
1518*9f257f50SLaurent Pinchart 	.start_streaming = imx7_csi_video_start_streaming,
1519*9f257f50SLaurent Pinchart 	.stop_streaming  = imx7_csi_video_stop_streaming,
1520*9f257f50SLaurent Pinchart };
1521*9f257f50SLaurent Pinchart 
1522*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
1523*9f257f50SLaurent Pinchart  * Video Capture Device - File Operations
1524*9f257f50SLaurent Pinchart  */
1525*9f257f50SLaurent Pinchart 
1526*9f257f50SLaurent Pinchart static int imx7_csi_video_open(struct file *file)
1527*9f257f50SLaurent Pinchart {
1528*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = video_drvdata(file);
1529*9f257f50SLaurent Pinchart 	int ret;
1530*9f257f50SLaurent Pinchart 
1531*9f257f50SLaurent Pinchart 	if (mutex_lock_interruptible(&csi->vdev_mutex))
1532*9f257f50SLaurent Pinchart 		return -ERESTARTSYS;
1533*9f257f50SLaurent Pinchart 
1534*9f257f50SLaurent Pinchart 	ret = v4l2_fh_open(file);
1535*9f257f50SLaurent Pinchart 	if (ret) {
1536*9f257f50SLaurent Pinchart 		dev_err(csi->dev, "v4l2_fh_open failed\n");
1537*9f257f50SLaurent Pinchart 		goto out;
1538*9f257f50SLaurent Pinchart 	}
1539*9f257f50SLaurent Pinchart 
1540*9f257f50SLaurent Pinchart 	ret = v4l2_pipeline_pm_get(&csi->vdev->entity);
1541*9f257f50SLaurent Pinchart 	if (ret)
1542*9f257f50SLaurent Pinchart 		v4l2_fh_release(file);
1543*9f257f50SLaurent Pinchart 
1544*9f257f50SLaurent Pinchart out:
1545*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->vdev_mutex);
1546*9f257f50SLaurent Pinchart 	return ret;
1547*9f257f50SLaurent Pinchart }
1548*9f257f50SLaurent Pinchart 
1549*9f257f50SLaurent Pinchart static int imx7_csi_video_release(struct file *file)
1550*9f257f50SLaurent Pinchart {
1551*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = video_drvdata(file);
1552*9f257f50SLaurent Pinchart 	struct vb2_queue *vq = &csi->q;
1553*9f257f50SLaurent Pinchart 
1554*9f257f50SLaurent Pinchart 	mutex_lock(&csi->vdev_mutex);
1555*9f257f50SLaurent Pinchart 
1556*9f257f50SLaurent Pinchart 	if (file->private_data == vq->owner) {
1557*9f257f50SLaurent Pinchart 		vb2_queue_release(vq);
1558*9f257f50SLaurent Pinchart 		vq->owner = NULL;
1559*9f257f50SLaurent Pinchart 	}
1560*9f257f50SLaurent Pinchart 
1561*9f257f50SLaurent Pinchart 	v4l2_pipeline_pm_put(&csi->vdev->entity);
1562*9f257f50SLaurent Pinchart 
1563*9f257f50SLaurent Pinchart 	v4l2_fh_release(file);
1564*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->vdev_mutex);
1565*9f257f50SLaurent Pinchart 	return 0;
1566*9f257f50SLaurent Pinchart }
1567*9f257f50SLaurent Pinchart 
1568*9f257f50SLaurent Pinchart static const struct v4l2_file_operations imx7_csi_video_fops = {
1569*9f257f50SLaurent Pinchart 	.owner		= THIS_MODULE,
1570*9f257f50SLaurent Pinchart 	.open		= imx7_csi_video_open,
1571*9f257f50SLaurent Pinchart 	.release	= imx7_csi_video_release,
1572*9f257f50SLaurent Pinchart 	.poll		= vb2_fop_poll,
1573*9f257f50SLaurent Pinchart 	.unlocked_ioctl	= video_ioctl2,
1574*9f257f50SLaurent Pinchart 	.mmap		= vb2_fop_mmap,
1575*9f257f50SLaurent Pinchart };
1576*9f257f50SLaurent Pinchart 
1577*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
1578*9f257f50SLaurent Pinchart  * Video Capture Device - Init & Cleanup
1579*9f257f50SLaurent Pinchart  */
1580*9f257f50SLaurent Pinchart 
1581*9f257f50SLaurent Pinchart static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi)
1582*9f257f50SLaurent Pinchart {
1583*9f257f50SLaurent Pinchart 	struct imx7_csi_vb2_buffer *buf = NULL;
1584*9f257f50SLaurent Pinchart 	unsigned long flags;
1585*9f257f50SLaurent Pinchart 
1586*9f257f50SLaurent Pinchart 	spin_lock_irqsave(&csi->q_lock, flags);
1587*9f257f50SLaurent Pinchart 
1588*9f257f50SLaurent Pinchart 	/* get next queued buffer */
1589*9f257f50SLaurent Pinchart 	if (!list_empty(&csi->ready_q)) {
1590*9f257f50SLaurent Pinchart 		buf = list_entry(csi->ready_q.next, struct imx7_csi_vb2_buffer,
1591*9f257f50SLaurent Pinchart 				 list);
1592*9f257f50SLaurent Pinchart 		list_del(&buf->list);
1593*9f257f50SLaurent Pinchart 	}
1594*9f257f50SLaurent Pinchart 
1595*9f257f50SLaurent Pinchart 	spin_unlock_irqrestore(&csi->q_lock, flags);
1596*9f257f50SLaurent Pinchart 
1597*9f257f50SLaurent Pinchart 	return buf;
1598*9f257f50SLaurent Pinchart }
1599*9f257f50SLaurent Pinchart 
1600*9f257f50SLaurent Pinchart static int imx7_csi_video_init_format(struct imx7_csi *csi)
1601*9f257f50SLaurent Pinchart {
1602*9f257f50SLaurent Pinchart 	struct v4l2_subdev_format fmt_src = {
1603*9f257f50SLaurent Pinchart 		.pad = IMX7_CSI_PAD_SRC,
1604*9f257f50SLaurent Pinchart 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
1605*9f257f50SLaurent Pinchart 	};
1606*9f257f50SLaurent Pinchart 	fmt_src.format.code = IMX7_CSI_DEF_MBUS_CODE;
1607*9f257f50SLaurent Pinchart 	fmt_src.format.width = IMX7_CSI_DEF_PIX_WIDTH;
1608*9f257f50SLaurent Pinchart 	fmt_src.format.height = IMX7_CSI_DEF_PIX_HEIGHT;
1609*9f257f50SLaurent Pinchart 
1610*9f257f50SLaurent Pinchart 	imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &fmt_src.format, NULL);
1611*9f257f50SLaurent Pinchart 	csi->vdev_compose.width = fmt_src.format.width;
1612*9f257f50SLaurent Pinchart 	csi->vdev_compose.height = fmt_src.format.height;
1613*9f257f50SLaurent Pinchart 
1614*9f257f50SLaurent Pinchart 	csi->vdev_cc = imx7_csi_find_pixel_format(csi->vdev_fmt.pixelformat);
1615*9f257f50SLaurent Pinchart 
1616*9f257f50SLaurent Pinchart 	return 0;
1617*9f257f50SLaurent Pinchart }
1618*9f257f50SLaurent Pinchart 
1619*9f257f50SLaurent Pinchart static int imx7_csi_video_register(struct imx7_csi *csi)
1620*9f257f50SLaurent Pinchart {
1621*9f257f50SLaurent Pinchart 	struct v4l2_subdev *sd = &csi->sd;
1622*9f257f50SLaurent Pinchart 	struct v4l2_device *v4l2_dev = sd->v4l2_dev;
1623*9f257f50SLaurent Pinchart 	struct video_device *vdev = csi->vdev;
1624*9f257f50SLaurent Pinchart 	int ret;
1625*9f257f50SLaurent Pinchart 
1626*9f257f50SLaurent Pinchart 	vdev->v4l2_dev = v4l2_dev;
1627*9f257f50SLaurent Pinchart 
1628*9f257f50SLaurent Pinchart 	/* Initialize the default format and compose rectangle. */
1629*9f257f50SLaurent Pinchart 	ret = imx7_csi_video_init_format(csi);
1630*9f257f50SLaurent Pinchart 	if (ret < 0)
1631*9f257f50SLaurent Pinchart 		return ret;
1632*9f257f50SLaurent Pinchart 
1633*9f257f50SLaurent Pinchart 	/* Register the video device. */
1634*9f257f50SLaurent Pinchart 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
1635*9f257f50SLaurent Pinchart 	if (ret) {
1636*9f257f50SLaurent Pinchart 		dev_err(csi->dev, "Failed to register video device\n");
1637*9f257f50SLaurent Pinchart 		return ret;
1638*9f257f50SLaurent Pinchart 	}
1639*9f257f50SLaurent Pinchart 
1640*9f257f50SLaurent Pinchart 	dev_info(csi->dev, "Registered %s as /dev/%s\n", vdev->name,
1641*9f257f50SLaurent Pinchart 		 video_device_node_name(vdev));
1642*9f257f50SLaurent Pinchart 
1643*9f257f50SLaurent Pinchart 	/* Create the link from the CSI subdev to the video device. */
1644*9f257f50SLaurent Pinchart 	ret = media_create_pad_link(&sd->entity, IMX7_CSI_PAD_SRC,
1645*9f257f50SLaurent Pinchart 				    &vdev->entity, 0, MEDIA_LNK_FL_IMMUTABLE |
1646*9f257f50SLaurent Pinchart 				    MEDIA_LNK_FL_ENABLED);
1647*9f257f50SLaurent Pinchart 	if (ret) {
1648*9f257f50SLaurent Pinchart 		dev_err(csi->dev, "failed to create link to device node\n");
1649*9f257f50SLaurent Pinchart 		video_unregister_device(vdev);
1650*9f257f50SLaurent Pinchart 		return ret;
1651*9f257f50SLaurent Pinchart 	}
1652*9f257f50SLaurent Pinchart 
1653*9f257f50SLaurent Pinchart 	return 0;
1654*9f257f50SLaurent Pinchart }
1655*9f257f50SLaurent Pinchart 
1656*9f257f50SLaurent Pinchart static void imx7_csi_video_unregister(struct imx7_csi *csi)
1657*9f257f50SLaurent Pinchart {
1658*9f257f50SLaurent Pinchart 	media_entity_cleanup(&csi->vdev->entity);
1659*9f257f50SLaurent Pinchart 	video_unregister_device(csi->vdev);
1660*9f257f50SLaurent Pinchart }
1661*9f257f50SLaurent Pinchart 
1662*9f257f50SLaurent Pinchart static int imx7_csi_video_init(struct imx7_csi *csi)
1663*9f257f50SLaurent Pinchart {
1664*9f257f50SLaurent Pinchart 	struct video_device *vdev;
1665*9f257f50SLaurent Pinchart 	struct vb2_queue *vq;
1666*9f257f50SLaurent Pinchart 	int ret;
1667*9f257f50SLaurent Pinchart 
1668*9f257f50SLaurent Pinchart 	mutex_init(&csi->vdev_mutex);
1669*9f257f50SLaurent Pinchart 	INIT_LIST_HEAD(&csi->ready_q);
1670*9f257f50SLaurent Pinchart 	spin_lock_init(&csi->q_lock);
1671*9f257f50SLaurent Pinchart 
1672*9f257f50SLaurent Pinchart 	/* Allocate and initialize the video device. */
1673*9f257f50SLaurent Pinchart 	vdev = video_device_alloc();
1674*9f257f50SLaurent Pinchart 	if (!vdev)
1675*9f257f50SLaurent Pinchart 		return -ENOMEM;
1676*9f257f50SLaurent Pinchart 
1677*9f257f50SLaurent Pinchart 	vdev->fops = &imx7_csi_video_fops;
1678*9f257f50SLaurent Pinchart 	vdev->ioctl_ops = &imx7_csi_video_ioctl_ops;
1679*9f257f50SLaurent Pinchart 	vdev->minor = -1;
1680*9f257f50SLaurent Pinchart 	vdev->release = video_device_release;
1681*9f257f50SLaurent Pinchart 	vdev->vfl_dir = VFL_DIR_RX;
1682*9f257f50SLaurent Pinchart 	vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
1683*9f257f50SLaurent Pinchart 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
1684*9f257f50SLaurent Pinchart 			 | V4L2_CAP_IO_MC;
1685*9f257f50SLaurent Pinchart 	vdev->lock = &csi->vdev_mutex;
1686*9f257f50SLaurent Pinchart 	vdev->queue = &csi->q;
1687*9f257f50SLaurent Pinchart 
1688*9f257f50SLaurent Pinchart 	snprintf(vdev->name, sizeof(vdev->name), "%s capture", csi->sd.name);
1689*9f257f50SLaurent Pinchart 
1690*9f257f50SLaurent Pinchart 	video_set_drvdata(vdev, csi);
1691*9f257f50SLaurent Pinchart 	csi->vdev = vdev;
1692*9f257f50SLaurent Pinchart 
1693*9f257f50SLaurent Pinchart 	/* Initialize the video device pad. */
1694*9f257f50SLaurent Pinchart 	csi->vdev_pad.flags = MEDIA_PAD_FL_SINK;
1695*9f257f50SLaurent Pinchart 	ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad);
1696*9f257f50SLaurent Pinchart 	if (ret) {
1697*9f257f50SLaurent Pinchart 		video_device_release(vdev);
1698*9f257f50SLaurent Pinchart 		return ret;
1699*9f257f50SLaurent Pinchart 	}
1700*9f257f50SLaurent Pinchart 
1701*9f257f50SLaurent Pinchart 	/* Initialize the vb2 queue. */
1702*9f257f50SLaurent Pinchart 	vq = &csi->q;
1703*9f257f50SLaurent Pinchart 	vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1704*9f257f50SLaurent Pinchart 	vq->io_modes = VB2_MMAP | VB2_DMABUF;
1705*9f257f50SLaurent Pinchart 	vq->drv_priv = csi;
1706*9f257f50SLaurent Pinchart 	vq->buf_struct_size = sizeof(struct imx7_csi_vb2_buffer);
1707*9f257f50SLaurent Pinchart 	vq->ops = &imx7_csi_video_qops;
1708*9f257f50SLaurent Pinchart 	vq->mem_ops = &vb2_dma_contig_memops;
1709*9f257f50SLaurent Pinchart 	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1710*9f257f50SLaurent Pinchart 	vq->lock = &csi->vdev_mutex;
1711*9f257f50SLaurent Pinchart 	vq->min_buffers_needed = 2;
1712*9f257f50SLaurent Pinchart 	vq->dev = csi->dev;
1713*9f257f50SLaurent Pinchart 
1714*9f257f50SLaurent Pinchart 	ret = vb2_queue_init(vq);
1715*9f257f50SLaurent Pinchart 	if (ret) {
1716*9f257f50SLaurent Pinchart 		dev_err(csi->dev, "vb2_queue_init failed\n");
1717*9f257f50SLaurent Pinchart 		video_device_release(vdev);
1718*9f257f50SLaurent Pinchart 		return ret;
1719*9f257f50SLaurent Pinchart 	}
1720*9f257f50SLaurent Pinchart 
1721*9f257f50SLaurent Pinchart 	return 0;
1722*9f257f50SLaurent Pinchart }
1723*9f257f50SLaurent Pinchart 
1724*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
1725*9f257f50SLaurent Pinchart  * V4L2 Subdev Operations
1726*9f257f50SLaurent Pinchart  */
1727*9f257f50SLaurent Pinchart 
1728*9f257f50SLaurent Pinchart static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable)
1729*9f257f50SLaurent Pinchart {
1730*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
1731*9f257f50SLaurent Pinchart 	int ret = 0;
1732*9f257f50SLaurent Pinchart 
1733*9f257f50SLaurent Pinchart 	mutex_lock(&csi->lock);
1734*9f257f50SLaurent Pinchart 
1735*9f257f50SLaurent Pinchart 	if (!csi->src_sd) {
1736*9f257f50SLaurent Pinchart 		ret = -EPIPE;
1737*9f257f50SLaurent Pinchart 		goto out_unlock;
1738*9f257f50SLaurent Pinchart 	}
1739*9f257f50SLaurent Pinchart 
1740*9f257f50SLaurent Pinchart 	if (csi->is_streaming == !!enable)
1741*9f257f50SLaurent Pinchart 		goto out_unlock;
1742*9f257f50SLaurent Pinchart 
1743*9f257f50SLaurent Pinchart 	if (enable) {
1744*9f257f50SLaurent Pinchart 		ret = imx7_csi_init(csi);
1745*9f257f50SLaurent Pinchart 		if (ret < 0)
1746*9f257f50SLaurent Pinchart 			goto out_unlock;
1747*9f257f50SLaurent Pinchart 
1748*9f257f50SLaurent Pinchart 		ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1);
1749*9f257f50SLaurent Pinchart 		if (ret < 0) {
1750*9f257f50SLaurent Pinchart 			imx7_csi_deinit(csi, VB2_BUF_STATE_QUEUED);
1751*9f257f50SLaurent Pinchart 			goto out_unlock;
1752*9f257f50SLaurent Pinchart 		}
1753*9f257f50SLaurent Pinchart 
1754*9f257f50SLaurent Pinchart 		imx7_csi_enable(csi);
1755*9f257f50SLaurent Pinchart 	} else {
1756*9f257f50SLaurent Pinchart 		imx7_csi_disable(csi);
1757*9f257f50SLaurent Pinchart 
1758*9f257f50SLaurent Pinchart 		v4l2_subdev_call(csi->src_sd, video, s_stream, 0);
1759*9f257f50SLaurent Pinchart 
1760*9f257f50SLaurent Pinchart 		imx7_csi_deinit(csi, VB2_BUF_STATE_ERROR);
1761*9f257f50SLaurent Pinchart 	}
1762*9f257f50SLaurent Pinchart 
1763*9f257f50SLaurent Pinchart 	csi->is_streaming = !!enable;
1764*9f257f50SLaurent Pinchart 
1765*9f257f50SLaurent Pinchart out_unlock:
1766*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->lock);
1767*9f257f50SLaurent Pinchart 
1768*9f257f50SLaurent Pinchart 	return ret;
1769*9f257f50SLaurent Pinchart }
1770*9f257f50SLaurent Pinchart 
1771*9f257f50SLaurent Pinchart static struct v4l2_mbus_framefmt *
1772*9f257f50SLaurent Pinchart imx7_csi_get_format(struct imx7_csi *csi,
1773*9f257f50SLaurent Pinchart 		    struct v4l2_subdev_state *sd_state,
1774*9f257f50SLaurent Pinchart 		    unsigned int pad,
1775*9f257f50SLaurent Pinchart 		    enum v4l2_subdev_format_whence which)
1776*9f257f50SLaurent Pinchart {
1777*9f257f50SLaurent Pinchart 	if (which == V4L2_SUBDEV_FORMAT_TRY)
1778*9f257f50SLaurent Pinchart 		return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad);
1779*9f257f50SLaurent Pinchart 
1780*9f257f50SLaurent Pinchart 	return &csi->format_mbus[pad];
1781*9f257f50SLaurent Pinchart }
1782*9f257f50SLaurent Pinchart 
1783*9f257f50SLaurent Pinchart static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
1784*9f257f50SLaurent Pinchart 			     struct v4l2_subdev_state *sd_state)
1785*9f257f50SLaurent Pinchart {
1786*9f257f50SLaurent Pinchart 	const enum v4l2_subdev_format_whence which =
1787*9f257f50SLaurent Pinchart 		sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
1788*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
1789*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1790*9f257f50SLaurent Pinchart 	int i;
1791*9f257f50SLaurent Pinchart 
1792*9f257f50SLaurent Pinchart 	cc = imx7_csi_find_mbus_format(IMX7_CSI_DEF_MBUS_CODE);
1793*9f257f50SLaurent Pinchart 
1794*9f257f50SLaurent Pinchart 	for (i = 0; i < IMX7_CSI_PADS_NUM; i++) {
1795*9f257f50SLaurent Pinchart 		struct v4l2_mbus_framefmt *mf =
1796*9f257f50SLaurent Pinchart 			imx7_csi_get_format(csi, sd_state, i, which);
1797*9f257f50SLaurent Pinchart 
1798*9f257f50SLaurent Pinchart 		mf->code = IMX7_CSI_DEF_MBUS_CODE;
1799*9f257f50SLaurent Pinchart 		mf->width = IMX7_CSI_DEF_PIX_WIDTH;
1800*9f257f50SLaurent Pinchart 		mf->height = IMX7_CSI_DEF_PIX_HEIGHT;
1801*9f257f50SLaurent Pinchart 		mf->field = V4L2_FIELD_NONE;
1802*9f257f50SLaurent Pinchart 
1803*9f257f50SLaurent Pinchart 		mf->colorspace = V4L2_COLORSPACE_SRGB;
1804*9f257f50SLaurent Pinchart 		mf->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mf->colorspace);
1805*9f257f50SLaurent Pinchart 		mf->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mf->colorspace);
1806*9f257f50SLaurent Pinchart 		mf->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(!cc->yuv,
1807*9f257f50SLaurent Pinchart 					mf->colorspace, mf->ycbcr_enc);
1808*9f257f50SLaurent Pinchart 
1809*9f257f50SLaurent Pinchart 		csi->cc[i] = cc;
1810*9f257f50SLaurent Pinchart 	}
1811*9f257f50SLaurent Pinchart 
1812*9f257f50SLaurent Pinchart 	return 0;
1813*9f257f50SLaurent Pinchart }
1814*9f257f50SLaurent Pinchart 
1815*9f257f50SLaurent Pinchart static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd,
1816*9f257f50SLaurent Pinchart 				   struct v4l2_subdev_state *sd_state,
1817*9f257f50SLaurent Pinchart 				   struct v4l2_subdev_mbus_code_enum *code)
1818*9f257f50SLaurent Pinchart {
1819*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
1820*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt *in_fmt;
1821*9f257f50SLaurent Pinchart 	int ret = 0;
1822*9f257f50SLaurent Pinchart 
1823*9f257f50SLaurent Pinchart 	mutex_lock(&csi->lock);
1824*9f257f50SLaurent Pinchart 
1825*9f257f50SLaurent Pinchart 	in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK,
1826*9f257f50SLaurent Pinchart 				     code->which);
1827*9f257f50SLaurent Pinchart 
1828*9f257f50SLaurent Pinchart 	switch (code->pad) {
1829*9f257f50SLaurent Pinchart 	case IMX7_CSI_PAD_SINK:
1830*9f257f50SLaurent Pinchart 		ret = imx7_csi_enum_mbus_formats(&code->code, code->index);
1831*9f257f50SLaurent Pinchart 		break;
1832*9f257f50SLaurent Pinchart 	case IMX7_CSI_PAD_SRC:
1833*9f257f50SLaurent Pinchart 		if (code->index != 0) {
1834*9f257f50SLaurent Pinchart 			ret = -EINVAL;
1835*9f257f50SLaurent Pinchart 			goto out_unlock;
1836*9f257f50SLaurent Pinchart 		}
1837*9f257f50SLaurent Pinchart 
1838*9f257f50SLaurent Pinchart 		code->code = in_fmt->code;
1839*9f257f50SLaurent Pinchart 		break;
1840*9f257f50SLaurent Pinchart 	default:
1841*9f257f50SLaurent Pinchart 		ret = -EINVAL;
1842*9f257f50SLaurent Pinchart 	}
1843*9f257f50SLaurent Pinchart 
1844*9f257f50SLaurent Pinchart out_unlock:
1845*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->lock);
1846*9f257f50SLaurent Pinchart 
1847*9f257f50SLaurent Pinchart 	return ret;
1848*9f257f50SLaurent Pinchart }
1849*9f257f50SLaurent Pinchart 
1850*9f257f50SLaurent Pinchart static int imx7_csi_get_fmt(struct v4l2_subdev *sd,
1851*9f257f50SLaurent Pinchart 			    struct v4l2_subdev_state *sd_state,
1852*9f257f50SLaurent Pinchart 			    struct v4l2_subdev_format *sdformat)
1853*9f257f50SLaurent Pinchart {
1854*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
1855*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt *fmt;
1856*9f257f50SLaurent Pinchart 	int ret = 0;
1857*9f257f50SLaurent Pinchart 
1858*9f257f50SLaurent Pinchart 	mutex_lock(&csi->lock);
1859*9f257f50SLaurent Pinchart 
1860*9f257f50SLaurent Pinchart 	fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad,
1861*9f257f50SLaurent Pinchart 				  sdformat->which);
1862*9f257f50SLaurent Pinchart 	if (!fmt) {
1863*9f257f50SLaurent Pinchart 		ret = -EINVAL;
1864*9f257f50SLaurent Pinchart 		goto out_unlock;
1865*9f257f50SLaurent Pinchart 	}
1866*9f257f50SLaurent Pinchart 
1867*9f257f50SLaurent Pinchart 	sdformat->format = *fmt;
1868*9f257f50SLaurent Pinchart 
1869*9f257f50SLaurent Pinchart out_unlock:
1870*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->lock);
1871*9f257f50SLaurent Pinchart 
1872*9f257f50SLaurent Pinchart 	return ret;
1873*9f257f50SLaurent Pinchart }
1874*9f257f50SLaurent Pinchart 
1875*9f257f50SLaurent Pinchart /*
1876*9f257f50SLaurent Pinchart  * Default the colorspace in tryfmt to SRGB if set to an unsupported
1877*9f257f50SLaurent Pinchart  * colorspace or not initialized. Then set the remaining colorimetry
1878*9f257f50SLaurent Pinchart  * parameters based on the colorspace if they are uninitialized.
1879*9f257f50SLaurent Pinchart  *
1880*9f257f50SLaurent Pinchart  * tryfmt->code must be set on entry.
1881*9f257f50SLaurent Pinchart  */
1882*9f257f50SLaurent Pinchart static void imx7_csi_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt)
1883*9f257f50SLaurent Pinchart {
1884*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1885*9f257f50SLaurent Pinchart 	bool is_rgb = false;
1886*9f257f50SLaurent Pinchart 
1887*9f257f50SLaurent Pinchart 	cc = imx7_csi_find_mbus_format(tryfmt->code);
1888*9f257f50SLaurent Pinchart 	if (cc && !cc->yuv)
1889*9f257f50SLaurent Pinchart 		is_rgb = true;
1890*9f257f50SLaurent Pinchart 
1891*9f257f50SLaurent Pinchart 	switch (tryfmt->colorspace) {
1892*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_SMPTE170M:
1893*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_REC709:
1894*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_JPEG:
1895*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_SRGB:
1896*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_BT2020:
1897*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_OPRGB:
1898*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_DCI_P3:
1899*9f257f50SLaurent Pinchart 	case V4L2_COLORSPACE_RAW:
1900*9f257f50SLaurent Pinchart 		break;
1901*9f257f50SLaurent Pinchart 	default:
1902*9f257f50SLaurent Pinchart 		tryfmt->colorspace = V4L2_COLORSPACE_SRGB;
1903*9f257f50SLaurent Pinchart 		break;
1904*9f257f50SLaurent Pinchart 	}
1905*9f257f50SLaurent Pinchart 
1906*9f257f50SLaurent Pinchart 	if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT)
1907*9f257f50SLaurent Pinchart 		tryfmt->xfer_func =
1908*9f257f50SLaurent Pinchart 			V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
1909*9f257f50SLaurent Pinchart 
1910*9f257f50SLaurent Pinchart 	if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
1911*9f257f50SLaurent Pinchart 		tryfmt->ycbcr_enc =
1912*9f257f50SLaurent Pinchart 			V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
1913*9f257f50SLaurent Pinchart 
1914*9f257f50SLaurent Pinchart 	if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT)
1915*9f257f50SLaurent Pinchart 		tryfmt->quantization =
1916*9f257f50SLaurent Pinchart 			V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb,
1917*9f257f50SLaurent Pinchart 						      tryfmt->colorspace,
1918*9f257f50SLaurent Pinchart 						      tryfmt->ycbcr_enc);
1919*9f257f50SLaurent Pinchart }
1920*9f257f50SLaurent Pinchart 
1921*9f257f50SLaurent Pinchart static int imx7_csi_try_fmt(struct imx7_csi *csi,
1922*9f257f50SLaurent Pinchart 			    struct v4l2_subdev_state *sd_state,
1923*9f257f50SLaurent Pinchart 			    struct v4l2_subdev_format *sdformat,
1924*9f257f50SLaurent Pinchart 			    const struct imx7_csi_pixfmt **cc)
1925*9f257f50SLaurent Pinchart {
1926*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *in_cc;
1927*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt *in_fmt;
1928*9f257f50SLaurent Pinchart 	u32 code;
1929*9f257f50SLaurent Pinchart 
1930*9f257f50SLaurent Pinchart 	in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK,
1931*9f257f50SLaurent Pinchart 				     sdformat->which);
1932*9f257f50SLaurent Pinchart 	if (!in_fmt)
1933*9f257f50SLaurent Pinchart 		return -EINVAL;
1934*9f257f50SLaurent Pinchart 
1935*9f257f50SLaurent Pinchart 	switch (sdformat->pad) {
1936*9f257f50SLaurent Pinchart 	case IMX7_CSI_PAD_SRC:
1937*9f257f50SLaurent Pinchart 		in_cc = imx7_csi_find_mbus_format(in_fmt->code);
1938*9f257f50SLaurent Pinchart 
1939*9f257f50SLaurent Pinchart 		sdformat->format.width = in_fmt->width;
1940*9f257f50SLaurent Pinchart 		sdformat->format.height = in_fmt->height;
1941*9f257f50SLaurent Pinchart 		sdformat->format.code = in_fmt->code;
1942*9f257f50SLaurent Pinchart 		sdformat->format.field = in_fmt->field;
1943*9f257f50SLaurent Pinchart 		*cc = in_cc;
1944*9f257f50SLaurent Pinchart 
1945*9f257f50SLaurent Pinchart 		sdformat->format.colorspace = in_fmt->colorspace;
1946*9f257f50SLaurent Pinchart 		sdformat->format.xfer_func = in_fmt->xfer_func;
1947*9f257f50SLaurent Pinchart 		sdformat->format.quantization = in_fmt->quantization;
1948*9f257f50SLaurent Pinchart 		sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc;
1949*9f257f50SLaurent Pinchart 		break;
1950*9f257f50SLaurent Pinchart 	case IMX7_CSI_PAD_SINK:
1951*9f257f50SLaurent Pinchart 		*cc = imx7_csi_find_mbus_format(sdformat->format.code);
1952*9f257f50SLaurent Pinchart 		if (!*cc) {
1953*9f257f50SLaurent Pinchart 			code = IMX7_CSI_DEF_MBUS_CODE;
1954*9f257f50SLaurent Pinchart 			*cc = imx7_csi_find_mbus_format(code);
1955*9f257f50SLaurent Pinchart 			sdformat->format.code = code;
1956*9f257f50SLaurent Pinchart 		}
1957*9f257f50SLaurent Pinchart 
1958*9f257f50SLaurent Pinchart 		if (sdformat->format.field != V4L2_FIELD_INTERLACED)
1959*9f257f50SLaurent Pinchart 			sdformat->format.field = V4L2_FIELD_NONE;
1960*9f257f50SLaurent Pinchart 		break;
1961*9f257f50SLaurent Pinchart 	default:
1962*9f257f50SLaurent Pinchart 		return -EINVAL;
1963*9f257f50SLaurent Pinchart 	}
1964*9f257f50SLaurent Pinchart 
1965*9f257f50SLaurent Pinchart 	imx7_csi_try_colorimetry(&sdformat->format);
1966*9f257f50SLaurent Pinchart 
1967*9f257f50SLaurent Pinchart 	return 0;
1968*9f257f50SLaurent Pinchart }
1969*9f257f50SLaurent Pinchart 
1970*9f257f50SLaurent Pinchart static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
1971*9f257f50SLaurent Pinchart 			    struct v4l2_subdev_state *sd_state,
1972*9f257f50SLaurent Pinchart 			    struct v4l2_subdev_format *sdformat)
1973*9f257f50SLaurent Pinchart {
1974*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
1975*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *outcc;
1976*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt *outfmt;
1977*9f257f50SLaurent Pinchart 	const struct imx7_csi_pixfmt *cc;
1978*9f257f50SLaurent Pinchart 	struct v4l2_mbus_framefmt *fmt;
1979*9f257f50SLaurent Pinchart 	struct v4l2_subdev_format format;
1980*9f257f50SLaurent Pinchart 	int ret = 0;
1981*9f257f50SLaurent Pinchart 
1982*9f257f50SLaurent Pinchart 	if (sdformat->pad >= IMX7_CSI_PADS_NUM)
1983*9f257f50SLaurent Pinchart 		return -EINVAL;
1984*9f257f50SLaurent Pinchart 
1985*9f257f50SLaurent Pinchart 	mutex_lock(&csi->lock);
1986*9f257f50SLaurent Pinchart 
1987*9f257f50SLaurent Pinchart 	if (csi->is_streaming) {
1988*9f257f50SLaurent Pinchart 		ret = -EBUSY;
1989*9f257f50SLaurent Pinchart 		goto out_unlock;
1990*9f257f50SLaurent Pinchart 	}
1991*9f257f50SLaurent Pinchart 
1992*9f257f50SLaurent Pinchart 	ret = imx7_csi_try_fmt(csi, sd_state, sdformat, &cc);
1993*9f257f50SLaurent Pinchart 	if (ret < 0)
1994*9f257f50SLaurent Pinchart 		goto out_unlock;
1995*9f257f50SLaurent Pinchart 
1996*9f257f50SLaurent Pinchart 	fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad,
1997*9f257f50SLaurent Pinchart 				  sdformat->which);
1998*9f257f50SLaurent Pinchart 	if (!fmt) {
1999*9f257f50SLaurent Pinchart 		ret = -EINVAL;
2000*9f257f50SLaurent Pinchart 		goto out_unlock;
2001*9f257f50SLaurent Pinchart 	}
2002*9f257f50SLaurent Pinchart 
2003*9f257f50SLaurent Pinchart 	*fmt = sdformat->format;
2004*9f257f50SLaurent Pinchart 
2005*9f257f50SLaurent Pinchart 	if (sdformat->pad == IMX7_CSI_PAD_SINK) {
2006*9f257f50SLaurent Pinchart 		/* propagate format to source pads */
2007*9f257f50SLaurent Pinchart 		format.pad = IMX7_CSI_PAD_SRC;
2008*9f257f50SLaurent Pinchart 		format.which = sdformat->which;
2009*9f257f50SLaurent Pinchart 		format.format = sdformat->format;
2010*9f257f50SLaurent Pinchart 		if (imx7_csi_try_fmt(csi, sd_state, &format, &outcc)) {
2011*9f257f50SLaurent Pinchart 			ret = -EINVAL;
2012*9f257f50SLaurent Pinchart 			goto out_unlock;
2013*9f257f50SLaurent Pinchart 		}
2014*9f257f50SLaurent Pinchart 		outfmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SRC,
2015*9f257f50SLaurent Pinchart 					     sdformat->which);
2016*9f257f50SLaurent Pinchart 		*outfmt = format.format;
2017*9f257f50SLaurent Pinchart 
2018*9f257f50SLaurent Pinchart 		if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
2019*9f257f50SLaurent Pinchart 			csi->cc[IMX7_CSI_PAD_SRC] = outcc;
2020*9f257f50SLaurent Pinchart 	}
2021*9f257f50SLaurent Pinchart 
2022*9f257f50SLaurent Pinchart 	if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
2023*9f257f50SLaurent Pinchart 		csi->cc[sdformat->pad] = cc;
2024*9f257f50SLaurent Pinchart 
2025*9f257f50SLaurent Pinchart out_unlock:
2026*9f257f50SLaurent Pinchart 	mutex_unlock(&csi->lock);
2027*9f257f50SLaurent Pinchart 
2028*9f257f50SLaurent Pinchart 	return ret;
2029*9f257f50SLaurent Pinchart }
2030*9f257f50SLaurent Pinchart 
2031*9f257f50SLaurent Pinchart static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd,
2032*9f257f50SLaurent Pinchart 				      struct media_link *link,
2033*9f257f50SLaurent Pinchart 				      struct v4l2_subdev_format *source_fmt,
2034*9f257f50SLaurent Pinchart 				      struct v4l2_subdev_format *sink_fmt)
2035*9f257f50SLaurent Pinchart {
2036*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
2037*9f257f50SLaurent Pinchart 	struct media_pad *pad = NULL;
2038*9f257f50SLaurent Pinchart 	unsigned int i;
2039*9f257f50SLaurent Pinchart 	int ret;
2040*9f257f50SLaurent Pinchart 
2041*9f257f50SLaurent Pinchart 	if (!csi->src_sd)
2042*9f257f50SLaurent Pinchart 		return -EPIPE;
2043*9f257f50SLaurent Pinchart 
2044*9f257f50SLaurent Pinchart 	/*
2045*9f257f50SLaurent Pinchart 	 * Validate the source link, and record whether the source uses the
2046*9f257f50SLaurent Pinchart 	 * parallel input or the CSI-2 receiver.
2047*9f257f50SLaurent Pinchart 	 */
2048*9f257f50SLaurent Pinchart 	ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt);
2049*9f257f50SLaurent Pinchart 	if (ret)
2050*9f257f50SLaurent Pinchart 		return ret;
2051*9f257f50SLaurent Pinchart 
2052*9f257f50SLaurent Pinchart 	switch (csi->src_sd->entity.function) {
2053*9f257f50SLaurent Pinchart 	case MEDIA_ENT_F_VID_IF_BRIDGE:
2054*9f257f50SLaurent Pinchart 		/* The input is the CSI-2 receiver. */
2055*9f257f50SLaurent Pinchart 		csi->is_csi2 = true;
2056*9f257f50SLaurent Pinchart 		break;
2057*9f257f50SLaurent Pinchart 
2058*9f257f50SLaurent Pinchart 	case MEDIA_ENT_F_VID_MUX:
2059*9f257f50SLaurent Pinchart 		/* The input is the mux, check its input. */
2060*9f257f50SLaurent Pinchart 		for (i = 0; i < csi->src_sd->entity.num_pads; i++) {
2061*9f257f50SLaurent Pinchart 			struct media_pad *spad = &csi->src_sd->entity.pads[i];
2062*9f257f50SLaurent Pinchart 
2063*9f257f50SLaurent Pinchart 			if (!(spad->flags & MEDIA_PAD_FL_SINK))
2064*9f257f50SLaurent Pinchart 				continue;
2065*9f257f50SLaurent Pinchart 
2066*9f257f50SLaurent Pinchart 			pad = media_pad_remote_pad_first(spad);
2067*9f257f50SLaurent Pinchart 			if (pad)
2068*9f257f50SLaurent Pinchart 				break;
2069*9f257f50SLaurent Pinchart 		}
2070*9f257f50SLaurent Pinchart 
2071*9f257f50SLaurent Pinchart 		if (!pad)
2072*9f257f50SLaurent Pinchart 			return -ENODEV;
2073*9f257f50SLaurent Pinchart 
2074*9f257f50SLaurent Pinchart 		csi->is_csi2 = pad->entity->function == MEDIA_ENT_F_VID_IF_BRIDGE;
2075*9f257f50SLaurent Pinchart 		break;
2076*9f257f50SLaurent Pinchart 
2077*9f257f50SLaurent Pinchart 	default:
2078*9f257f50SLaurent Pinchart 		/*
2079*9f257f50SLaurent Pinchart 		 * The input is an external entity, it must use the parallel
2080*9f257f50SLaurent Pinchart 		 * bus.
2081*9f257f50SLaurent Pinchart 		 */
2082*9f257f50SLaurent Pinchart 		csi->is_csi2 = false;
2083*9f257f50SLaurent Pinchart 		break;
2084*9f257f50SLaurent Pinchart 	}
2085*9f257f50SLaurent Pinchart 
2086*9f257f50SLaurent Pinchart 	return 0;
2087*9f257f50SLaurent Pinchart }
2088*9f257f50SLaurent Pinchart 
2089*9f257f50SLaurent Pinchart static int imx7_csi_registered(struct v4l2_subdev *sd)
2090*9f257f50SLaurent Pinchart {
2091*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
2092*9f257f50SLaurent Pinchart 	int ret;
2093*9f257f50SLaurent Pinchart 
2094*9f257f50SLaurent Pinchart 	ret = imx7_csi_video_init(csi);
2095*9f257f50SLaurent Pinchart 	if (ret)
2096*9f257f50SLaurent Pinchart 		return ret;
2097*9f257f50SLaurent Pinchart 
2098*9f257f50SLaurent Pinchart 	ret = imx7_csi_video_register(csi);
2099*9f257f50SLaurent Pinchart 	if (ret)
2100*9f257f50SLaurent Pinchart 		return ret;
2101*9f257f50SLaurent Pinchart 
2102*9f257f50SLaurent Pinchart 	ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
2103*9f257f50SLaurent Pinchart 	if (ret)
2104*9f257f50SLaurent Pinchart 		goto err_unreg;
2105*9f257f50SLaurent Pinchart 
2106*9f257f50SLaurent Pinchart 	ret = media_device_register(&csi->mdev);
2107*9f257f50SLaurent Pinchart 	if (ret)
2108*9f257f50SLaurent Pinchart 		goto err_unreg;
2109*9f257f50SLaurent Pinchart 
2110*9f257f50SLaurent Pinchart 	return 0;
2111*9f257f50SLaurent Pinchart 
2112*9f257f50SLaurent Pinchart err_unreg:
2113*9f257f50SLaurent Pinchart 	imx7_csi_video_unregister(csi);
2114*9f257f50SLaurent Pinchart 	return ret;
2115*9f257f50SLaurent Pinchart }
2116*9f257f50SLaurent Pinchart 
2117*9f257f50SLaurent Pinchart static void imx7_csi_unregistered(struct v4l2_subdev *sd)
2118*9f257f50SLaurent Pinchart {
2119*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = v4l2_get_subdevdata(sd);
2120*9f257f50SLaurent Pinchart 
2121*9f257f50SLaurent Pinchart 	imx7_csi_video_unregister(csi);
2122*9f257f50SLaurent Pinchart }
2123*9f257f50SLaurent Pinchart 
2124*9f257f50SLaurent Pinchart static const struct v4l2_subdev_video_ops imx7_csi_video_ops = {
2125*9f257f50SLaurent Pinchart 	.s_stream	= imx7_csi_s_stream,
2126*9f257f50SLaurent Pinchart };
2127*9f257f50SLaurent Pinchart 
2128*9f257f50SLaurent Pinchart static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = {
2129*9f257f50SLaurent Pinchart 	.init_cfg	= imx7_csi_init_cfg,
2130*9f257f50SLaurent Pinchart 	.enum_mbus_code	= imx7_csi_enum_mbus_code,
2131*9f257f50SLaurent Pinchart 	.get_fmt	= imx7_csi_get_fmt,
2132*9f257f50SLaurent Pinchart 	.set_fmt	= imx7_csi_set_fmt,
2133*9f257f50SLaurent Pinchart 	.link_validate	= imx7_csi_pad_link_validate,
2134*9f257f50SLaurent Pinchart };
2135*9f257f50SLaurent Pinchart 
2136*9f257f50SLaurent Pinchart static const struct v4l2_subdev_ops imx7_csi_subdev_ops = {
2137*9f257f50SLaurent Pinchart 	.video		= &imx7_csi_video_ops,
2138*9f257f50SLaurent Pinchart 	.pad		= &imx7_csi_pad_ops,
2139*9f257f50SLaurent Pinchart };
2140*9f257f50SLaurent Pinchart 
2141*9f257f50SLaurent Pinchart static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = {
2142*9f257f50SLaurent Pinchart 	.registered	= imx7_csi_registered,
2143*9f257f50SLaurent Pinchart 	.unregistered	= imx7_csi_unregistered,
2144*9f257f50SLaurent Pinchart };
2145*9f257f50SLaurent Pinchart 
2146*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
2147*9f257f50SLaurent Pinchart  * Media Entity Operations
2148*9f257f50SLaurent Pinchart  */
2149*9f257f50SLaurent Pinchart 
2150*9f257f50SLaurent Pinchart static const struct media_entity_operations imx7_csi_entity_ops = {
2151*9f257f50SLaurent Pinchart 	.link_validate	= v4l2_subdev_link_validate,
2152*9f257f50SLaurent Pinchart 	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
2153*9f257f50SLaurent Pinchart };
2154*9f257f50SLaurent Pinchart 
2155*9f257f50SLaurent Pinchart /* -----------------------------------------------------------------------------
2156*9f257f50SLaurent Pinchart  * Probe & Remove
2157*9f257f50SLaurent Pinchart  */
2158*9f257f50SLaurent Pinchart 
2159*9f257f50SLaurent Pinchart static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier,
2160*9f257f50SLaurent Pinchart 				 struct v4l2_subdev *sd,
2161*9f257f50SLaurent Pinchart 				 struct v4l2_async_subdev *asd)
2162*9f257f50SLaurent Pinchart {
2163*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier);
2164*9f257f50SLaurent Pinchart 	struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK];
2165*9f257f50SLaurent Pinchart 
2166*9f257f50SLaurent Pinchart 	csi->src_sd = sd;
2167*9f257f50SLaurent Pinchart 
2168*9f257f50SLaurent Pinchart 	return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
2169*9f257f50SLaurent Pinchart 					       MEDIA_LNK_FL_IMMUTABLE);
2170*9f257f50SLaurent Pinchart }
2171*9f257f50SLaurent Pinchart 
2172*9f257f50SLaurent Pinchart static int imx7_csi_notify_complete(struct v4l2_async_notifier *notifier)
2173*9f257f50SLaurent Pinchart {
2174*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier);
2175*9f257f50SLaurent Pinchart 
2176*9f257f50SLaurent Pinchart 	return v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
2177*9f257f50SLaurent Pinchart }
2178*9f257f50SLaurent Pinchart 
2179*9f257f50SLaurent Pinchart static const struct v4l2_async_notifier_operations imx7_csi_notify_ops = {
2180*9f257f50SLaurent Pinchart 	.bound = imx7_csi_notify_bound,
2181*9f257f50SLaurent Pinchart 	.complete = imx7_csi_notify_complete,
2182*9f257f50SLaurent Pinchart };
2183*9f257f50SLaurent Pinchart 
2184*9f257f50SLaurent Pinchart static int imx7_csi_async_register(struct imx7_csi *csi)
2185*9f257f50SLaurent Pinchart {
2186*9f257f50SLaurent Pinchart 	struct v4l2_async_subdev *asd;
2187*9f257f50SLaurent Pinchart 	struct fwnode_handle *ep;
2188*9f257f50SLaurent Pinchart 	int ret;
2189*9f257f50SLaurent Pinchart 
2190*9f257f50SLaurent Pinchart 	v4l2_async_nf_init(&csi->notifier);
2191*9f257f50SLaurent Pinchart 
2192*9f257f50SLaurent Pinchart 	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0,
2193*9f257f50SLaurent Pinchart 					     FWNODE_GRAPH_ENDPOINT_NEXT);
2194*9f257f50SLaurent Pinchart 	if (ep) {
2195*9f257f50SLaurent Pinchart 		asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep,
2196*9f257f50SLaurent Pinchart 						      struct v4l2_async_subdev);
2197*9f257f50SLaurent Pinchart 
2198*9f257f50SLaurent Pinchart 		fwnode_handle_put(ep);
2199*9f257f50SLaurent Pinchart 
2200*9f257f50SLaurent Pinchart 		if (IS_ERR(asd)) {
2201*9f257f50SLaurent Pinchart 			ret = PTR_ERR(asd);
2202*9f257f50SLaurent Pinchart 			/* OK if asd already exists */
2203*9f257f50SLaurent Pinchart 			if (ret != -EEXIST)
2204*9f257f50SLaurent Pinchart 				return ret;
2205*9f257f50SLaurent Pinchart 		}
2206*9f257f50SLaurent Pinchart 	}
2207*9f257f50SLaurent Pinchart 
2208*9f257f50SLaurent Pinchart 	csi->notifier.ops = &imx7_csi_notify_ops;
2209*9f257f50SLaurent Pinchart 
2210*9f257f50SLaurent Pinchart 	ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier);
2211*9f257f50SLaurent Pinchart 	if (ret)
2212*9f257f50SLaurent Pinchart 		return ret;
2213*9f257f50SLaurent Pinchart 
2214*9f257f50SLaurent Pinchart 	return 0;
2215*9f257f50SLaurent Pinchart }
2216*9f257f50SLaurent Pinchart 
2217*9f257f50SLaurent Pinchart static void imx7_csi_media_cleanup(struct imx7_csi *csi)
2218*9f257f50SLaurent Pinchart {
2219*9f257f50SLaurent Pinchart 	v4l2_device_unregister(&csi->v4l2_dev);
2220*9f257f50SLaurent Pinchart 	media_device_unregister(&csi->mdev);
2221*9f257f50SLaurent Pinchart 	media_device_cleanup(&csi->mdev);
2222*9f257f50SLaurent Pinchart }
2223*9f257f50SLaurent Pinchart 
2224*9f257f50SLaurent Pinchart static const struct media_device_ops imx7_csi_media_ops = {
2225*9f257f50SLaurent Pinchart 	.link_notify = v4l2_pipeline_link_notify,
2226*9f257f50SLaurent Pinchart };
2227*9f257f50SLaurent Pinchart 
2228*9f257f50SLaurent Pinchart static int imx7_csi_media_dev_init(struct imx7_csi *csi)
2229*9f257f50SLaurent Pinchart {
2230*9f257f50SLaurent Pinchart 	int ret;
2231*9f257f50SLaurent Pinchart 
2232*9f257f50SLaurent Pinchart 	strscpy(csi->mdev.model, "imx-media", sizeof(csi->mdev.model));
2233*9f257f50SLaurent Pinchart 	csi->mdev.ops = &imx7_csi_media_ops;
2234*9f257f50SLaurent Pinchart 	csi->mdev.dev = csi->dev;
2235*9f257f50SLaurent Pinchart 
2236*9f257f50SLaurent Pinchart 	csi->v4l2_dev.mdev = &csi->mdev;
2237*9f257f50SLaurent Pinchart 	strscpy(csi->v4l2_dev.name, "imx-media",
2238*9f257f50SLaurent Pinchart 		sizeof(csi->v4l2_dev.name));
2239*9f257f50SLaurent Pinchart 	snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info),
2240*9f257f50SLaurent Pinchart 		 "platform:%s", dev_name(csi->mdev.dev));
2241*9f257f50SLaurent Pinchart 
2242*9f257f50SLaurent Pinchart 	media_device_init(&csi->mdev);
2243*9f257f50SLaurent Pinchart 
2244*9f257f50SLaurent Pinchart 	ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
2245*9f257f50SLaurent Pinchart 	if (ret < 0) {
2246*9f257f50SLaurent Pinchart 		v4l2_err(&csi->v4l2_dev,
2247*9f257f50SLaurent Pinchart 			 "Failed to register v4l2_device: %d\n", ret);
2248*9f257f50SLaurent Pinchart 		goto cleanup;
2249*9f257f50SLaurent Pinchart 	}
2250*9f257f50SLaurent Pinchart 
2251*9f257f50SLaurent Pinchart 	return 0;
2252*9f257f50SLaurent Pinchart 
2253*9f257f50SLaurent Pinchart cleanup:
2254*9f257f50SLaurent Pinchart 	media_device_cleanup(&csi->mdev);
2255*9f257f50SLaurent Pinchart 
2256*9f257f50SLaurent Pinchart 	return ret;
2257*9f257f50SLaurent Pinchart }
2258*9f257f50SLaurent Pinchart 
2259*9f257f50SLaurent Pinchart static int imx7_csi_media_init(struct imx7_csi *csi)
2260*9f257f50SLaurent Pinchart {
2261*9f257f50SLaurent Pinchart 	unsigned int i;
2262*9f257f50SLaurent Pinchart 	int ret;
2263*9f257f50SLaurent Pinchart 
2264*9f257f50SLaurent Pinchart 	/* add media device */
2265*9f257f50SLaurent Pinchart 	ret = imx7_csi_media_dev_init(csi);
2266*9f257f50SLaurent Pinchart 	if (ret)
2267*9f257f50SLaurent Pinchart 		return ret;
2268*9f257f50SLaurent Pinchart 
2269*9f257f50SLaurent Pinchart 	v4l2_subdev_init(&csi->sd, &imx7_csi_subdev_ops);
2270*9f257f50SLaurent Pinchart 	v4l2_set_subdevdata(&csi->sd, csi);
2271*9f257f50SLaurent Pinchart 	csi->sd.internal_ops = &imx7_csi_internal_ops;
2272*9f257f50SLaurent Pinchart 	csi->sd.entity.ops = &imx7_csi_entity_ops;
2273*9f257f50SLaurent Pinchart 	csi->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
2274*9f257f50SLaurent Pinchart 	csi->sd.dev = csi->dev;
2275*9f257f50SLaurent Pinchart 	csi->sd.owner = THIS_MODULE;
2276*9f257f50SLaurent Pinchart 	csi->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
2277*9f257f50SLaurent Pinchart 	snprintf(csi->sd.name, sizeof(csi->sd.name), "csi");
2278*9f257f50SLaurent Pinchart 
2279*9f257f50SLaurent Pinchart 	for (i = 0; i < IMX7_CSI_PADS_NUM; i++)
2280*9f257f50SLaurent Pinchart 		csi->pad[i].flags = (i == IMX7_CSI_PAD_SINK) ?
2281*9f257f50SLaurent Pinchart 			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
2282*9f257f50SLaurent Pinchart 
2283*9f257f50SLaurent Pinchart 	ret = media_entity_pads_init(&csi->sd.entity, IMX7_CSI_PADS_NUM,
2284*9f257f50SLaurent Pinchart 				     csi->pad);
2285*9f257f50SLaurent Pinchart 	if (ret)
2286*9f257f50SLaurent Pinchart 		goto error;
2287*9f257f50SLaurent Pinchart 
2288*9f257f50SLaurent Pinchart 	ret = v4l2_device_register_subdev(&csi->v4l2_dev, &csi->sd);
2289*9f257f50SLaurent Pinchart 	if (ret)
2290*9f257f50SLaurent Pinchart 		goto error;
2291*9f257f50SLaurent Pinchart 
2292*9f257f50SLaurent Pinchart 	return 0;
2293*9f257f50SLaurent Pinchart 
2294*9f257f50SLaurent Pinchart error:
2295*9f257f50SLaurent Pinchart 	imx7_csi_media_cleanup(csi);
2296*9f257f50SLaurent Pinchart 	return ret;
2297*9f257f50SLaurent Pinchart }
2298*9f257f50SLaurent Pinchart 
2299*9f257f50SLaurent Pinchart static int imx7_csi_probe(struct platform_device *pdev)
2300*9f257f50SLaurent Pinchart {
2301*9f257f50SLaurent Pinchart 	struct device *dev = &pdev->dev;
2302*9f257f50SLaurent Pinchart 	struct imx7_csi *csi;
2303*9f257f50SLaurent Pinchart 	int ret;
2304*9f257f50SLaurent Pinchart 
2305*9f257f50SLaurent Pinchart 	csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
2306*9f257f50SLaurent Pinchart 	if (!csi)
2307*9f257f50SLaurent Pinchart 		return -ENOMEM;
2308*9f257f50SLaurent Pinchart 
2309*9f257f50SLaurent Pinchart 	csi->dev = dev;
2310*9f257f50SLaurent Pinchart 	platform_set_drvdata(pdev, csi);
2311*9f257f50SLaurent Pinchart 
2312*9f257f50SLaurent Pinchart 	spin_lock_init(&csi->irqlock);
2313*9f257f50SLaurent Pinchart 	mutex_init(&csi->lock);
2314*9f257f50SLaurent Pinchart 
2315*9f257f50SLaurent Pinchart 	/* Acquire resources and install interrupt handler. */
2316*9f257f50SLaurent Pinchart 	csi->mclk = devm_clk_get(&pdev->dev, "mclk");
2317*9f257f50SLaurent Pinchart 	if (IS_ERR(csi->mclk)) {
2318*9f257f50SLaurent Pinchart 		ret = PTR_ERR(csi->mclk);
2319*9f257f50SLaurent Pinchart 		dev_err(dev, "Failed to get mclk: %d", ret);
2320*9f257f50SLaurent Pinchart 		goto destroy_mutex;
2321*9f257f50SLaurent Pinchart 	}
2322*9f257f50SLaurent Pinchart 
2323*9f257f50SLaurent Pinchart 	csi->irq = platform_get_irq(pdev, 0);
2324*9f257f50SLaurent Pinchart 	if (csi->irq < 0) {
2325*9f257f50SLaurent Pinchart 		ret = csi->irq;
2326*9f257f50SLaurent Pinchart 		goto destroy_mutex;
2327*9f257f50SLaurent Pinchart 	}
2328*9f257f50SLaurent Pinchart 
2329*9f257f50SLaurent Pinchart 	csi->regbase = devm_platform_ioremap_resource(pdev, 0);
2330*9f257f50SLaurent Pinchart 	if (IS_ERR(csi->regbase)) {
2331*9f257f50SLaurent Pinchart 		ret = PTR_ERR(csi->regbase);
2332*9f257f50SLaurent Pinchart 		goto destroy_mutex;
2333*9f257f50SLaurent Pinchart 	}
2334*9f257f50SLaurent Pinchart 
2335*9f257f50SLaurent Pinchart 	csi->model = (enum imx_csi_model)(uintptr_t)of_device_get_match_data(&pdev->dev);
2336*9f257f50SLaurent Pinchart 
2337*9f257f50SLaurent Pinchart 	ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi",
2338*9f257f50SLaurent Pinchart 			       (void *)csi);
2339*9f257f50SLaurent Pinchart 	if (ret < 0) {
2340*9f257f50SLaurent Pinchart 		dev_err(dev, "Request CSI IRQ failed.\n");
2341*9f257f50SLaurent Pinchart 		goto destroy_mutex;
2342*9f257f50SLaurent Pinchart 	}
2343*9f257f50SLaurent Pinchart 
2344*9f257f50SLaurent Pinchart 	/* Initialize all the media device infrastructure. */
2345*9f257f50SLaurent Pinchart 	ret = imx7_csi_media_init(csi);
2346*9f257f50SLaurent Pinchart 	if (ret)
2347*9f257f50SLaurent Pinchart 		goto destroy_mutex;
2348*9f257f50SLaurent Pinchart 
2349*9f257f50SLaurent Pinchart 	/* Set the default mbus formats. */
2350*9f257f50SLaurent Pinchart 	ret = imx7_csi_init_cfg(&csi->sd, NULL);
2351*9f257f50SLaurent Pinchart 	if (ret)
2352*9f257f50SLaurent Pinchart 		goto media_cleanup;
2353*9f257f50SLaurent Pinchart 
2354*9f257f50SLaurent Pinchart 	ret = imx7_csi_async_register(csi);
2355*9f257f50SLaurent Pinchart 	if (ret)
2356*9f257f50SLaurent Pinchart 		goto subdev_notifier_cleanup;
2357*9f257f50SLaurent Pinchart 
2358*9f257f50SLaurent Pinchart 	return 0;
2359*9f257f50SLaurent Pinchart 
2360*9f257f50SLaurent Pinchart subdev_notifier_cleanup:
2361*9f257f50SLaurent Pinchart 	v4l2_async_nf_unregister(&csi->notifier);
2362*9f257f50SLaurent Pinchart 	v4l2_async_nf_cleanup(&csi->notifier);
2363*9f257f50SLaurent Pinchart media_cleanup:
2364*9f257f50SLaurent Pinchart 	imx7_csi_media_cleanup(csi);
2365*9f257f50SLaurent Pinchart 
2366*9f257f50SLaurent Pinchart destroy_mutex:
2367*9f257f50SLaurent Pinchart 	mutex_destroy(&csi->lock);
2368*9f257f50SLaurent Pinchart 
2369*9f257f50SLaurent Pinchart 	return ret;
2370*9f257f50SLaurent Pinchart }
2371*9f257f50SLaurent Pinchart 
2372*9f257f50SLaurent Pinchart static int imx7_csi_remove(struct platform_device *pdev)
2373*9f257f50SLaurent Pinchart {
2374*9f257f50SLaurent Pinchart 	struct imx7_csi *csi = platform_get_drvdata(pdev);
2375*9f257f50SLaurent Pinchart 
2376*9f257f50SLaurent Pinchart 	imx7_csi_media_cleanup(csi);
2377*9f257f50SLaurent Pinchart 
2378*9f257f50SLaurent Pinchart 	v4l2_async_nf_unregister(&csi->notifier);
2379*9f257f50SLaurent Pinchart 	v4l2_async_nf_cleanup(&csi->notifier);
2380*9f257f50SLaurent Pinchart 	v4l2_async_unregister_subdev(&csi->sd);
2381*9f257f50SLaurent Pinchart 
2382*9f257f50SLaurent Pinchart 	mutex_destroy(&csi->lock);
2383*9f257f50SLaurent Pinchart 
2384*9f257f50SLaurent Pinchart 	return 0;
2385*9f257f50SLaurent Pinchart }
2386*9f257f50SLaurent Pinchart 
2387*9f257f50SLaurent Pinchart static const struct of_device_id imx7_csi_of_match[] = {
2388*9f257f50SLaurent Pinchart 	{ .compatible = "fsl,imx8mq-csi", .data = (void *)IMX7_CSI_IMX8MQ },
2389*9f257f50SLaurent Pinchart 	{ .compatible = "fsl,imx7-csi", .data = (void *)IMX7_CSI_IMX7 },
2390*9f257f50SLaurent Pinchart 	{ .compatible = "fsl,imx6ul-csi", .data = (void *)IMX7_CSI_IMX7 },
2391*9f257f50SLaurent Pinchart 	{ },
2392*9f257f50SLaurent Pinchart };
2393*9f257f50SLaurent Pinchart MODULE_DEVICE_TABLE(of, imx7_csi_of_match);
2394*9f257f50SLaurent Pinchart 
2395*9f257f50SLaurent Pinchart static struct platform_driver imx7_csi_driver = {
2396*9f257f50SLaurent Pinchart 	.probe = imx7_csi_probe,
2397*9f257f50SLaurent Pinchart 	.remove = imx7_csi_remove,
2398*9f257f50SLaurent Pinchart 	.driver = {
2399*9f257f50SLaurent Pinchart 		.of_match_table = imx7_csi_of_match,
2400*9f257f50SLaurent Pinchart 		.name = "imx7-csi",
2401*9f257f50SLaurent Pinchart 	},
2402*9f257f50SLaurent Pinchart };
2403*9f257f50SLaurent Pinchart module_platform_driver(imx7_csi_driver);
2404*9f257f50SLaurent Pinchart 
2405*9f257f50SLaurent Pinchart MODULE_DESCRIPTION("i.MX7 CSI subdev driver");
2406*9f257f50SLaurent Pinchart MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
2407*9f257f50SLaurent Pinchart MODULE_LICENSE("GPL v2");
2408*9f257f50SLaurent Pinchart MODULE_ALIAS("platform:imx7-csi");
2409