1b4a3d877SJai Luthra // SPDX-License-Identifier: GPL-2.0-only
2b4a3d877SJai Luthra /*
3b4a3d877SJai Luthra  * TI CSI2RX Shim Wrapper Driver
4b4a3d877SJai Luthra  *
5b4a3d877SJai Luthra  * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
6b4a3d877SJai Luthra  *
7b4a3d877SJai Luthra  * Author: Pratyush Yadav <p.yadav@ti.com>
8b4a3d877SJai Luthra  * Author: Jai Luthra <j-luthra@ti.com>
9b4a3d877SJai Luthra  */
10b4a3d877SJai Luthra 
11b4a3d877SJai Luthra #include <linux/bitfield.h>
12b4a3d877SJai Luthra #include <linux/dmaengine.h>
13b4a3d877SJai Luthra #include <linux/module.h>
14b4a3d877SJai Luthra #include <linux/of_platform.h>
15b4a3d877SJai Luthra #include <linux/platform_device.h>
16b4a3d877SJai Luthra 
17b4a3d877SJai Luthra #include <media/mipi-csi2.h>
18b4a3d877SJai Luthra #include <media/v4l2-device.h>
19b4a3d877SJai Luthra #include <media/v4l2-ioctl.h>
20b4a3d877SJai Luthra #include <media/v4l2-mc.h>
21b4a3d877SJai Luthra #include <media/videobuf2-dma-contig.h>
22b4a3d877SJai Luthra 
23b4a3d877SJai Luthra #define TI_CSI2RX_MODULE_NAME		"j721e-csi2rx"
24b4a3d877SJai Luthra 
25b4a3d877SJai Luthra #define SHIM_CNTL			0x10
26b4a3d877SJai Luthra #define SHIM_CNTL_PIX_RST		BIT(0)
27b4a3d877SJai Luthra 
28b4a3d877SJai Luthra #define SHIM_DMACNTX			0x20
29b4a3d877SJai Luthra #define SHIM_DMACNTX_EN			BIT(31)
30b4a3d877SJai Luthra #define SHIM_DMACNTX_YUV422		GENMASK(27, 26)
31b4a3d877SJai Luthra #define SHIM_DMACNTX_SIZE		GENMASK(21, 20)
32b4a3d877SJai Luthra #define SHIM_DMACNTX_FMT		GENMASK(5, 0)
33b4a3d877SJai Luthra #define SHIM_DMACNTX_YUV422_MODE_11	3
34b4a3d877SJai Luthra #define SHIM_DMACNTX_SIZE_8		0
35b4a3d877SJai Luthra #define SHIM_DMACNTX_SIZE_16		1
36b4a3d877SJai Luthra #define SHIM_DMACNTX_SIZE_32		2
37b4a3d877SJai Luthra 
38b4a3d877SJai Luthra #define SHIM_PSI_CFG0			0x24
39b4a3d877SJai Luthra #define SHIM_PSI_CFG0_SRC_TAG		GENMASK(15, 0)
40b4a3d877SJai Luthra #define SHIM_PSI_CFG0_DST_TAG		GENMASK(31, 16)
41b4a3d877SJai Luthra 
42b4a3d877SJai Luthra #define PSIL_WORD_SIZE_BYTES		16
43b4a3d877SJai Luthra /*
44b4a3d877SJai Luthra  * There are no hard limits on the width or height. The DMA engine can handle
45b4a3d877SJai Luthra  * all sizes. The max width and height are arbitrary numbers for this driver.
46b4a3d877SJai Luthra  * Use 16K * 16K as the arbitrary limit. It is large enough that it is unlikely
47b4a3d877SJai Luthra  * the limit will be hit in practice.
48b4a3d877SJai Luthra  */
49b4a3d877SJai Luthra #define MAX_WIDTH_BYTES			SZ_16K
50b4a3d877SJai Luthra #define MAX_HEIGHT_LINES		SZ_16K
51b4a3d877SJai Luthra 
52b4a3d877SJai Luthra #define DRAIN_TIMEOUT_MS		50
53b4a3d877SJai Luthra #define DRAIN_BUFFER_SIZE		SZ_32K
54b4a3d877SJai Luthra 
55b4a3d877SJai Luthra struct ti_csi2rx_fmt {
56b4a3d877SJai Luthra 	u32				fourcc;	/* Four character code. */
57b4a3d877SJai Luthra 	u32				code;	/* Mbus code. */
58b4a3d877SJai Luthra 	u32				csi_dt;	/* CSI Data type. */
59b4a3d877SJai Luthra 	u8				bpp;	/* Bits per pixel. */
60b4a3d877SJai Luthra 	u8				size;	/* Data size shift when unpacking. */
61b4a3d877SJai Luthra };
62b4a3d877SJai Luthra 
63b4a3d877SJai Luthra struct ti_csi2rx_buffer {
64b4a3d877SJai Luthra 	/* Common v4l2 buffer. Must be first. */
65b4a3d877SJai Luthra 	struct vb2_v4l2_buffer		vb;
66b4a3d877SJai Luthra 	struct list_head		list;
67b4a3d877SJai Luthra 	struct ti_csi2rx_dev		*csi;
68b4a3d877SJai Luthra };
69b4a3d877SJai Luthra 
70b4a3d877SJai Luthra enum ti_csi2rx_dma_state {
71b4a3d877SJai Luthra 	TI_CSI2RX_DMA_STOPPED,	/* Streaming not started yet. */
72b4a3d877SJai Luthra 	TI_CSI2RX_DMA_IDLE,	/* Streaming but no pending DMA operation. */
73b4a3d877SJai Luthra 	TI_CSI2RX_DMA_ACTIVE,	/* Streaming and pending DMA operation. */
74b4a3d877SJai Luthra };
75b4a3d877SJai Luthra 
76b4a3d877SJai Luthra struct ti_csi2rx_dma {
77b4a3d877SJai Luthra 	/* Protects all fields in this struct. */
78b4a3d877SJai Luthra 	spinlock_t			lock;
79b4a3d877SJai Luthra 	struct dma_chan			*chan;
80b4a3d877SJai Luthra 	/* Buffers queued to the driver, waiting to be processed by DMA. */
81b4a3d877SJai Luthra 	struct list_head		queue;
82b4a3d877SJai Luthra 	enum ti_csi2rx_dma_state	state;
83b4a3d877SJai Luthra 	/*
84b4a3d877SJai Luthra 	 * Queue of buffers submitted to DMA engine.
85b4a3d877SJai Luthra 	 */
86b4a3d877SJai Luthra 	struct list_head		submitted;
87b4a3d877SJai Luthra 	/* Buffer to drain stale data from PSI-L endpoint */
88b4a3d877SJai Luthra 	struct {
89b4a3d877SJai Luthra 		void			*vaddr;
90b4a3d877SJai Luthra 		dma_addr_t		paddr;
91b4a3d877SJai Luthra 		size_t			len;
92b4a3d877SJai Luthra 	} drain;
93b4a3d877SJai Luthra };
94b4a3d877SJai Luthra 
95b4a3d877SJai Luthra struct ti_csi2rx_dev {
96b4a3d877SJai Luthra 	struct device			*dev;
97b4a3d877SJai Luthra 	void __iomem			*shim;
98b4a3d877SJai Luthra 	struct v4l2_device		v4l2_dev;
99b4a3d877SJai Luthra 	struct video_device		vdev;
100b4a3d877SJai Luthra 	struct media_device		mdev;
101b4a3d877SJai Luthra 	struct media_pipeline		pipe;
102b4a3d877SJai Luthra 	struct media_pad		pad;
103b4a3d877SJai Luthra 	struct v4l2_async_notifier	notifier;
104b4a3d877SJai Luthra 	struct v4l2_subdev		*source;
105b4a3d877SJai Luthra 	struct vb2_queue		vidq;
106b4a3d877SJai Luthra 	struct mutex			mutex; /* To serialize ioctls. */
107b4a3d877SJai Luthra 	struct v4l2_format		v_fmt;
108b4a3d877SJai Luthra 	struct ti_csi2rx_dma		dma;
109b4a3d877SJai Luthra 	u32				sequence;
110b4a3d877SJai Luthra };
111b4a3d877SJai Luthra 
112b4a3d877SJai Luthra static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = {
113b4a3d877SJai Luthra 	{
114b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_YUYV,
115b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_YUYV8_1X16,
116b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_YUV422_8B,
117b4a3d877SJai Luthra 		.bpp			= 16,
118b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
119b4a3d877SJai Luthra 	}, {
120b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_UYVY,
121b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_UYVY8_1X16,
122b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_YUV422_8B,
123b4a3d877SJai Luthra 		.bpp			= 16,
124b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
125b4a3d877SJai Luthra 	}, {
126b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_YVYU,
127b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_YVYU8_1X16,
128b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_YUV422_8B,
129b4a3d877SJai Luthra 		.bpp			= 16,
130b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
131b4a3d877SJai Luthra 	}, {
132b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_VYUY,
133b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_VYUY8_1X16,
134b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_YUV422_8B,
135b4a3d877SJai Luthra 		.bpp			= 16,
136b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
137b4a3d877SJai Luthra 	}, {
138b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SBGGR8,
139b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SBGGR8_1X8,
140b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW8,
141b4a3d877SJai Luthra 		.bpp			= 8,
142b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
143b4a3d877SJai Luthra 	}, {
144b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SGBRG8,
145b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SGBRG8_1X8,
146b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW8,
147b4a3d877SJai Luthra 		.bpp			= 8,
148b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
149b4a3d877SJai Luthra 	}, {
150b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SGRBG8,
151b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SGRBG8_1X8,
152b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW8,
153b4a3d877SJai Luthra 		.bpp			= 8,
154b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
155b4a3d877SJai Luthra 	}, {
156b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SRGGB8,
157b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SRGGB8_1X8,
158b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW8,
159b4a3d877SJai Luthra 		.bpp			= 8,
160b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_8,
161b4a3d877SJai Luthra 	}, {
16224a4e402SJulien Massot 		.fourcc			= V4L2_PIX_FMT_GREY,
16324a4e402SJulien Massot 		.code			= MEDIA_BUS_FMT_Y8_1X8,
16424a4e402SJulien Massot 		.csi_dt			= MIPI_CSI2_DT_RAW8,
16524a4e402SJulien Massot 		.bpp			= 8,
16624a4e402SJulien Massot 		.size			= SHIM_DMACNTX_SIZE_8,
16724a4e402SJulien Massot 	}, {
168b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SBGGR10,
169b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SBGGR10_1X10,
170b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW10,
171b4a3d877SJai Luthra 		.bpp			= 16,
172b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_16,
173b4a3d877SJai Luthra 	}, {
174b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SGBRG10,
175b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SGBRG10_1X10,
176b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW10,
177b4a3d877SJai Luthra 		.bpp			= 16,
178b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_16,
179b4a3d877SJai Luthra 	}, {
180b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SGRBG10,
181b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SGRBG10_1X10,
182b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW10,
183b4a3d877SJai Luthra 		.bpp			= 16,
184b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_16,
185b4a3d877SJai Luthra 	}, {
186b4a3d877SJai Luthra 		.fourcc			= V4L2_PIX_FMT_SRGGB10,
187b4a3d877SJai Luthra 		.code			= MEDIA_BUS_FMT_SRGGB10_1X10,
188b4a3d877SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RAW10,
189b4a3d877SJai Luthra 		.bpp			= 16,
190b4a3d877SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_16,
1915eb0ad46SJai Luthra 	}, {
1925eb0ad46SJai Luthra 		.fourcc			= V4L2_PIX_FMT_RGB565X,
1935eb0ad46SJai Luthra 		.code			= MEDIA_BUS_FMT_RGB565_1X16,
1945eb0ad46SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RGB565,
1955eb0ad46SJai Luthra 		.bpp			= 16,
1965eb0ad46SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_16,
1975eb0ad46SJai Luthra 	}, {
1985eb0ad46SJai Luthra 		.fourcc			= V4L2_PIX_FMT_XBGR32,
1995eb0ad46SJai Luthra 		.code			= MEDIA_BUS_FMT_RGB888_1X24,
2005eb0ad46SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RGB888,
2015eb0ad46SJai Luthra 		.bpp			= 32,
2025eb0ad46SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_32,
2035eb0ad46SJai Luthra 	}, {
2045eb0ad46SJai Luthra 		.fourcc			= V4L2_PIX_FMT_RGBX32,
2055eb0ad46SJai Luthra 		.code			= MEDIA_BUS_FMT_BGR888_1X24,
2065eb0ad46SJai Luthra 		.csi_dt			= MIPI_CSI2_DT_RGB888,
2075eb0ad46SJai Luthra 		.bpp			= 32,
2085eb0ad46SJai Luthra 		.size			= SHIM_DMACNTX_SIZE_32,
209b4a3d877SJai Luthra 	},
210b4a3d877SJai Luthra 
211b4a3d877SJai Luthra 	/* More formats can be supported but they are not listed for now. */
212b4a3d877SJai Luthra };
213b4a3d877SJai Luthra 
214b4a3d877SJai Luthra /* Forward declaration needed by ti_csi2rx_dma_callback. */
215b4a3d877SJai Luthra static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
216b4a3d877SJai Luthra 			       struct ti_csi2rx_buffer *buf);
217b4a3d877SJai Luthra 
find_format_by_fourcc(u32 pixelformat)218b4a3d877SJai Luthra static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat)
219b4a3d877SJai Luthra {
220b4a3d877SJai Luthra 	unsigned int i;
221b4a3d877SJai Luthra 
222b4a3d877SJai Luthra 	for (i = 0; i < ARRAY_SIZE(ti_csi2rx_formats); i++) {
223b4a3d877SJai Luthra 		if (ti_csi2rx_formats[i].fourcc == pixelformat)
224b4a3d877SJai Luthra 			return &ti_csi2rx_formats[i];
225b4a3d877SJai Luthra 	}
226b4a3d877SJai Luthra 
227b4a3d877SJai Luthra 	return NULL;
228b4a3d877SJai Luthra }
229b4a3d877SJai Luthra 
find_format_by_code(u32 code)230b4a3d877SJai Luthra static const struct ti_csi2rx_fmt *find_format_by_code(u32 code)
231b4a3d877SJai Luthra {
232b4a3d877SJai Luthra 	unsigned int i;
233b4a3d877SJai Luthra 
234b4a3d877SJai Luthra 	for (i = 0; i < ARRAY_SIZE(ti_csi2rx_formats); i++) {
235b4a3d877SJai Luthra 		if (ti_csi2rx_formats[i].code == code)
236b4a3d877SJai Luthra 			return &ti_csi2rx_formats[i];
237b4a3d877SJai Luthra 	}
238b4a3d877SJai Luthra 
239b4a3d877SJai Luthra 	return NULL;
240b4a3d877SJai Luthra }
241b4a3d877SJai Luthra 
ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt * csi_fmt,struct v4l2_format * v4l2_fmt)242b4a3d877SJai Luthra static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt,
243b4a3d877SJai Luthra 			       struct v4l2_format *v4l2_fmt)
244b4a3d877SJai Luthra {
245b4a3d877SJai Luthra 	struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix;
246b4a3d877SJai Luthra 	unsigned int pixels_in_word;
247b4a3d877SJai Luthra 
248b4a3d877SJai Luthra 	pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / csi_fmt->bpp;
249b4a3d877SJai Luthra 
250b4a3d877SJai Luthra 	/* Clamp width and height to sensible maximums (16K x 16K) */
251b4a3d877SJai Luthra 	pix->width = clamp_t(unsigned int, pix->width,
252b4a3d877SJai Luthra 			     pixels_in_word,
253b4a3d877SJai Luthra 			     MAX_WIDTH_BYTES * 8 / csi_fmt->bpp);
254b4a3d877SJai Luthra 	pix->height = clamp_t(unsigned int, pix->height, 1, MAX_HEIGHT_LINES);
255b4a3d877SJai Luthra 
256b4a3d877SJai Luthra 	/* Width should be a multiple of transfer word-size */
257b4a3d877SJai Luthra 	pix->width = rounddown(pix->width, pixels_in_word);
258b4a3d877SJai Luthra 
259b4a3d877SJai Luthra 	v4l2_fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
260b4a3d877SJai Luthra 	pix->pixelformat = csi_fmt->fourcc;
261b4a3d877SJai Luthra 	pix->bytesperline = pix->width * (csi_fmt->bpp / 8);
262b4a3d877SJai Luthra 	pix->sizeimage = pix->bytesperline * pix->height;
263b4a3d877SJai Luthra }
264b4a3d877SJai Luthra 
ti_csi2rx_querycap(struct file * file,void * priv,struct v4l2_capability * cap)265b4a3d877SJai Luthra static int ti_csi2rx_querycap(struct file *file, void *priv,
266b4a3d877SJai Luthra 			      struct v4l2_capability *cap)
267b4a3d877SJai Luthra {
268b4a3d877SJai Luthra 	strscpy(cap->driver, TI_CSI2RX_MODULE_NAME, sizeof(cap->driver));
269b4a3d877SJai Luthra 	strscpy(cap->card, TI_CSI2RX_MODULE_NAME, sizeof(cap->card));
270b4a3d877SJai Luthra 
271b4a3d877SJai Luthra 	return 0;
272b4a3d877SJai Luthra }
273b4a3d877SJai Luthra 
ti_csi2rx_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)274b4a3d877SJai Luthra static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv,
275b4a3d877SJai Luthra 				      struct v4l2_fmtdesc *f)
276b4a3d877SJai Luthra {
277b4a3d877SJai Luthra 	const struct ti_csi2rx_fmt *fmt = NULL;
278b4a3d877SJai Luthra 
279b4a3d877SJai Luthra 	if (f->mbus_code) {
280b4a3d877SJai Luthra 		/* 1-to-1 mapping between bus formats and pixel formats */
281b4a3d877SJai Luthra 		if (f->index > 0)
282b4a3d877SJai Luthra 			return -EINVAL;
283b4a3d877SJai Luthra 
284b4a3d877SJai Luthra 		fmt = find_format_by_code(f->mbus_code);
285b4a3d877SJai Luthra 	} else {
286b4a3d877SJai Luthra 		if (f->index >= ARRAY_SIZE(ti_csi2rx_formats))
287b4a3d877SJai Luthra 			return -EINVAL;
288b4a3d877SJai Luthra 
289b4a3d877SJai Luthra 		fmt = &ti_csi2rx_formats[f->index];
290b4a3d877SJai Luthra 	}
291b4a3d877SJai Luthra 
292b4a3d877SJai Luthra 	if (!fmt)
293b4a3d877SJai Luthra 		return -EINVAL;
294b4a3d877SJai Luthra 
295b4a3d877SJai Luthra 	f->pixelformat = fmt->fourcc;
296b4a3d877SJai Luthra 	memset(f->reserved, 0, sizeof(f->reserved));
297b4a3d877SJai Luthra 	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
298b4a3d877SJai Luthra 
299b4a3d877SJai Luthra 	return 0;
300b4a3d877SJai Luthra }
301b4a3d877SJai Luthra 
ti_csi2rx_g_fmt_vid_cap(struct file * file,void * prov,struct v4l2_format * f)302b4a3d877SJai Luthra static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *prov,
303b4a3d877SJai Luthra 				   struct v4l2_format *f)
304b4a3d877SJai Luthra {
305b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = video_drvdata(file);
306b4a3d877SJai Luthra 
307b4a3d877SJai Luthra 	*f = csi->v_fmt;
308b4a3d877SJai Luthra 
309b4a3d877SJai Luthra 	return 0;
310b4a3d877SJai Luthra }
311b4a3d877SJai Luthra 
ti_csi2rx_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)312b4a3d877SJai Luthra static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv,
313b4a3d877SJai Luthra 				     struct v4l2_format *f)
314b4a3d877SJai Luthra {
315b4a3d877SJai Luthra 	const struct ti_csi2rx_fmt *fmt;
316b4a3d877SJai Luthra 
317b4a3d877SJai Luthra 	/*
318b4a3d877SJai Luthra 	 * Default to the first format if the requested pixel format code isn't
319b4a3d877SJai Luthra 	 * supported.
320b4a3d877SJai Luthra 	 */
321b4a3d877SJai Luthra 	fmt = find_format_by_fourcc(f->fmt.pix.pixelformat);
322b4a3d877SJai Luthra 	if (!fmt)
323b4a3d877SJai Luthra 		fmt = &ti_csi2rx_formats[0];
324b4a3d877SJai Luthra 
325b4a3d877SJai Luthra 	/* Interlaced formats are not supported. */
326b4a3d877SJai Luthra 	f->fmt.pix.field = V4L2_FIELD_NONE;
327b4a3d877SJai Luthra 
328b4a3d877SJai Luthra 	ti_csi2rx_fill_fmt(fmt, f);
329b4a3d877SJai Luthra 
330b4a3d877SJai Luthra 	return 0;
331b4a3d877SJai Luthra }
332b4a3d877SJai Luthra 
ti_csi2rx_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)333b4a3d877SJai Luthra static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv,
334b4a3d877SJai Luthra 				   struct v4l2_format *f)
335b4a3d877SJai Luthra {
336b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = video_drvdata(file);
337b4a3d877SJai Luthra 	struct vb2_queue *q = &csi->vidq;
338b4a3d877SJai Luthra 	int ret;
339b4a3d877SJai Luthra 
340b4a3d877SJai Luthra 	if (vb2_is_busy(q))
341b4a3d877SJai Luthra 		return -EBUSY;
342b4a3d877SJai Luthra 
343b4a3d877SJai Luthra 	ret = ti_csi2rx_try_fmt_vid_cap(file, priv, f);
344b4a3d877SJai Luthra 	if (ret < 0)
345b4a3d877SJai Luthra 		return ret;
346b4a3d877SJai Luthra 
347b4a3d877SJai Luthra 	csi->v_fmt = *f;
348b4a3d877SJai Luthra 
349b4a3d877SJai Luthra 	return 0;
350b4a3d877SJai Luthra }
351b4a3d877SJai Luthra 
ti_csi2rx_enum_framesizes(struct file * file,void * fh,struct v4l2_frmsizeenum * fsize)352b4a3d877SJai Luthra static int ti_csi2rx_enum_framesizes(struct file *file, void *fh,
353b4a3d877SJai Luthra 				     struct v4l2_frmsizeenum *fsize)
354b4a3d877SJai Luthra {
355b4a3d877SJai Luthra 	const struct ti_csi2rx_fmt *fmt;
356b4a3d877SJai Luthra 	unsigned int pixels_in_word;
357b4a3d877SJai Luthra 
358b4a3d877SJai Luthra 	fmt = find_format_by_fourcc(fsize->pixel_format);
359b4a3d877SJai Luthra 	if (!fmt || fsize->index != 0)
360b4a3d877SJai Luthra 		return -EINVAL;
361b4a3d877SJai Luthra 
362b4a3d877SJai Luthra 	/*
363b4a3d877SJai Luthra 	 * Number of pixels in one PSI-L word. The transfer happens in multiples
364b4a3d877SJai Luthra 	 * of PSI-L word sizes.
365b4a3d877SJai Luthra 	 */
366b4a3d877SJai Luthra 	pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / fmt->bpp;
367b4a3d877SJai Luthra 
368b4a3d877SJai Luthra 	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
369b4a3d877SJai Luthra 	fsize->stepwise.min_width = pixels_in_word;
370b4a3d877SJai Luthra 	fsize->stepwise.max_width = rounddown(MAX_WIDTH_BYTES * 8 / fmt->bpp,
371b4a3d877SJai Luthra 					      pixels_in_word);
372b4a3d877SJai Luthra 	fsize->stepwise.step_width = pixels_in_word;
373b4a3d877SJai Luthra 	fsize->stepwise.min_height = 1;
374b4a3d877SJai Luthra 	fsize->stepwise.max_height = MAX_HEIGHT_LINES;
375b4a3d877SJai Luthra 	fsize->stepwise.step_height = 1;
376b4a3d877SJai Luthra 
377b4a3d877SJai Luthra 	return 0;
378b4a3d877SJai Luthra }
379b4a3d877SJai Luthra 
380b4a3d877SJai Luthra static const struct v4l2_ioctl_ops csi_ioctl_ops = {
381b4a3d877SJai Luthra 	.vidioc_querycap      = ti_csi2rx_querycap,
382b4a3d877SJai Luthra 	.vidioc_enum_fmt_vid_cap = ti_csi2rx_enum_fmt_vid_cap,
383b4a3d877SJai Luthra 	.vidioc_try_fmt_vid_cap = ti_csi2rx_try_fmt_vid_cap,
384b4a3d877SJai Luthra 	.vidioc_g_fmt_vid_cap = ti_csi2rx_g_fmt_vid_cap,
385b4a3d877SJai Luthra 	.vidioc_s_fmt_vid_cap = ti_csi2rx_s_fmt_vid_cap,
386b4a3d877SJai Luthra 	.vidioc_enum_framesizes = ti_csi2rx_enum_framesizes,
387b4a3d877SJai Luthra 	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
388b4a3d877SJai Luthra 	.vidioc_create_bufs   = vb2_ioctl_create_bufs,
389b4a3d877SJai Luthra 	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
390b4a3d877SJai Luthra 	.vidioc_querybuf      = vb2_ioctl_querybuf,
391b4a3d877SJai Luthra 	.vidioc_qbuf          = vb2_ioctl_qbuf,
392b4a3d877SJai Luthra 	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
393b4a3d877SJai Luthra 	.vidioc_expbuf        = vb2_ioctl_expbuf,
394b4a3d877SJai Luthra 	.vidioc_streamon      = vb2_ioctl_streamon,
395b4a3d877SJai Luthra 	.vidioc_streamoff     = vb2_ioctl_streamoff,
396b4a3d877SJai Luthra };
397b4a3d877SJai Luthra 
398b4a3d877SJai Luthra static const struct v4l2_file_operations csi_fops = {
399b4a3d877SJai Luthra 	.owner = THIS_MODULE,
400b4a3d877SJai Luthra 	.open = v4l2_fh_open,
401b4a3d877SJai Luthra 	.release = vb2_fop_release,
402b4a3d877SJai Luthra 	.read = vb2_fop_read,
403b4a3d877SJai Luthra 	.poll = vb2_fop_poll,
404b4a3d877SJai Luthra 	.unlocked_ioctl = video_ioctl2,
405b4a3d877SJai Luthra 	.mmap = vb2_fop_mmap,
406b4a3d877SJai Luthra };
407b4a3d877SJai Luthra 
csi_async_notifier_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_connection * asc)408b4a3d877SJai Luthra static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier,
409b4a3d877SJai Luthra 				    struct v4l2_subdev *subdev,
410b4a3d877SJai Luthra 				    struct v4l2_async_connection *asc)
411b4a3d877SJai Luthra {
412b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev);
413b4a3d877SJai Luthra 
414b4a3d877SJai Luthra 	csi->source = subdev;
415b4a3d877SJai Luthra 
416b4a3d877SJai Luthra 	return 0;
417b4a3d877SJai Luthra }
418b4a3d877SJai Luthra 
csi_async_notifier_complete(struct v4l2_async_notifier * notifier)419b4a3d877SJai Luthra static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier)
420b4a3d877SJai Luthra {
421b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev);
422b4a3d877SJai Luthra 	struct video_device *vdev = &csi->vdev;
423b4a3d877SJai Luthra 	int ret;
424b4a3d877SJai Luthra 
425b4a3d877SJai Luthra 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
426b4a3d877SJai Luthra 	if (ret)
427b4a3d877SJai Luthra 		return ret;
428b4a3d877SJai Luthra 
429b4a3d877SJai Luthra 	ret = v4l2_create_fwnode_links_to_pad(csi->source, &csi->pad,
430b4a3d877SJai Luthra 					      MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
431b4a3d877SJai Luthra 
432b4a3d877SJai Luthra 	if (ret) {
433b4a3d877SJai Luthra 		video_unregister_device(vdev);
434b4a3d877SJai Luthra 		return ret;
435b4a3d877SJai Luthra 	}
436b4a3d877SJai Luthra 
437b4a3d877SJai Luthra 	ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
438b4a3d877SJai Luthra 	if (ret)
439b4a3d877SJai Luthra 		video_unregister_device(vdev);
440b4a3d877SJai Luthra 
441b4a3d877SJai Luthra 	return ret;
442b4a3d877SJai Luthra }
443b4a3d877SJai Luthra 
444b4a3d877SJai Luthra static const struct v4l2_async_notifier_operations csi_async_notifier_ops = {
445b4a3d877SJai Luthra 	.bound = csi_async_notifier_bound,
446b4a3d877SJai Luthra 	.complete = csi_async_notifier_complete,
447b4a3d877SJai Luthra };
448b4a3d877SJai Luthra 
ti_csi2rx_notifier_register(struct ti_csi2rx_dev * csi)449b4a3d877SJai Luthra static int ti_csi2rx_notifier_register(struct ti_csi2rx_dev *csi)
450b4a3d877SJai Luthra {
451b4a3d877SJai Luthra 	struct fwnode_handle *fwnode;
452b4a3d877SJai Luthra 	struct v4l2_async_connection *asc;
453b4a3d877SJai Luthra 	struct device_node *node;
454b4a3d877SJai Luthra 	int ret;
455b4a3d877SJai Luthra 
456b4a3d877SJai Luthra 	node = of_get_child_by_name(csi->dev->of_node, "csi-bridge");
457b4a3d877SJai Luthra 	if (!node)
458b4a3d877SJai Luthra 		return -EINVAL;
459b4a3d877SJai Luthra 
460b4a3d877SJai Luthra 	fwnode = of_fwnode_handle(node);
461b4a3d877SJai Luthra 	if (!fwnode) {
462b4a3d877SJai Luthra 		of_node_put(node);
463b4a3d877SJai Luthra 		return -EINVAL;
464b4a3d877SJai Luthra 	}
465b4a3d877SJai Luthra 
466b4a3d877SJai Luthra 	v4l2_async_nf_init(&csi->notifier, &csi->v4l2_dev);
467b4a3d877SJai Luthra 	csi->notifier.ops = &csi_async_notifier_ops;
468b4a3d877SJai Luthra 
469b4a3d877SJai Luthra 	asc = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode,
470b4a3d877SJai Luthra 				       struct v4l2_async_connection);
471b4a3d877SJai Luthra 	of_node_put(node);
472b4a3d877SJai Luthra 	if (IS_ERR(asc)) {
473b4a3d877SJai Luthra 		v4l2_async_nf_cleanup(&csi->notifier);
474b4a3d877SJai Luthra 		return PTR_ERR(asc);
475b4a3d877SJai Luthra 	}
476b4a3d877SJai Luthra 
477b4a3d877SJai Luthra 	ret = v4l2_async_nf_register(&csi->notifier);
478b4a3d877SJai Luthra 	if (ret) {
479b4a3d877SJai Luthra 		v4l2_async_nf_cleanup(&csi->notifier);
480b4a3d877SJai Luthra 		return ret;
481b4a3d877SJai Luthra 	}
482b4a3d877SJai Luthra 
483b4a3d877SJai Luthra 	return 0;
484b4a3d877SJai Luthra }
485b4a3d877SJai Luthra 
ti_csi2rx_setup_shim(struct ti_csi2rx_dev * csi)486b4a3d877SJai Luthra static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi)
487b4a3d877SJai Luthra {
488b4a3d877SJai Luthra 	const struct ti_csi2rx_fmt *fmt;
489b4a3d877SJai Luthra 	unsigned int reg;
490b4a3d877SJai Luthra 
491b4a3d877SJai Luthra 	fmt = find_format_by_fourcc(csi->v_fmt.fmt.pix.pixelformat);
492b4a3d877SJai Luthra 
493b4a3d877SJai Luthra 	/* De-assert the pixel interface reset. */
494b4a3d877SJai Luthra 	reg = SHIM_CNTL_PIX_RST;
495b4a3d877SJai Luthra 	writel(reg, csi->shim + SHIM_CNTL);
496b4a3d877SJai Luthra 
497b4a3d877SJai Luthra 	reg = SHIM_DMACNTX_EN;
498b4a3d877SJai Luthra 	reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt);
499b4a3d877SJai Luthra 
500b4a3d877SJai Luthra 	/*
501b4a3d877SJai Luthra 	 * The hardware assumes incoming YUV422 8-bit data on MIPI CSI2 bus
502b4a3d877SJai Luthra 	 * follows the spec and is packed in the order U0 -> Y0 -> V0 -> Y1 ->
503b4a3d877SJai Luthra 	 * ...
504b4a3d877SJai Luthra 	 *
505b4a3d877SJai Luthra 	 * There is an option to swap the bytes around before storing in
506b4a3d877SJai Luthra 	 * memory, to achieve different pixel formats:
507b4a3d877SJai Luthra 	 *
508b4a3d877SJai Luthra 	 * Byte3 <----------- Byte0
509b4a3d877SJai Luthra 	 * [ Y1 ][ V0 ][ Y0 ][ U0 ]	MODE 11
510b4a3d877SJai Luthra 	 * [ Y1 ][ U0 ][ Y0 ][ V0 ]	MODE 10
511b4a3d877SJai Luthra 	 * [ V0 ][ Y1 ][ U0 ][ Y0 ]	MODE 01
512b4a3d877SJai Luthra 	 * [ U0 ][ Y1 ][ V0 ][ Y0 ]	MODE 00
513b4a3d877SJai Luthra 	 *
514b4a3d877SJai Luthra 	 * We don't have any requirement to change pixelformat from what is
515b4a3d877SJai Luthra 	 * coming from the source, so we keep it in MODE 11, which does not
516b4a3d877SJai Luthra 	 * swap any bytes when storing in memory.
517b4a3d877SJai Luthra 	 */
518b4a3d877SJai Luthra 	switch (fmt->fourcc) {
519b4a3d877SJai Luthra 	case V4L2_PIX_FMT_UYVY:
520b4a3d877SJai Luthra 	case V4L2_PIX_FMT_VYUY:
521b4a3d877SJai Luthra 	case V4L2_PIX_FMT_YUYV:
522b4a3d877SJai Luthra 	case V4L2_PIX_FMT_YVYU:
523b4a3d877SJai Luthra 		reg |= FIELD_PREP(SHIM_DMACNTX_YUV422,
524b4a3d877SJai Luthra 				  SHIM_DMACNTX_YUV422_MODE_11);
525b4a3d877SJai Luthra 		break;
526b4a3d877SJai Luthra 	default:
527b4a3d877SJai Luthra 		/* Ignore if not YUV 4:2:2 */
528b4a3d877SJai Luthra 		break;
529b4a3d877SJai Luthra 	}
530b4a3d877SJai Luthra 
531b4a3d877SJai Luthra 	reg |= FIELD_PREP(SHIM_DMACNTX_SIZE, fmt->size);
532b4a3d877SJai Luthra 
533b4a3d877SJai Luthra 	writel(reg, csi->shim + SHIM_DMACNTX);
534b4a3d877SJai Luthra 
535b4a3d877SJai Luthra 	reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) |
536b4a3d877SJai Luthra 	      FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 0);
537b4a3d877SJai Luthra 	writel(reg, csi->shim + SHIM_PSI_CFG0);
538b4a3d877SJai Luthra }
539b4a3d877SJai Luthra 
ti_csi2rx_drain_callback(void * param)540b4a3d877SJai Luthra static void ti_csi2rx_drain_callback(void *param)
541b4a3d877SJai Luthra {
542b4a3d877SJai Luthra 	struct completion *drain_complete = param;
543b4a3d877SJai Luthra 
544b4a3d877SJai Luthra 	complete(drain_complete);
545b4a3d877SJai Luthra }
546b4a3d877SJai Luthra 
547b4a3d877SJai Luthra /*
548b4a3d877SJai Luthra  * Drain the stale data left at the PSI-L endpoint.
549b4a3d877SJai Luthra  *
550b4a3d877SJai Luthra  * This might happen if no buffers are queued in time but source is still
551b4a3d877SJai Luthra  * streaming. In multi-stream scenarios this can happen when one stream is
552b4a3d877SJai Luthra  * stopped but other is still streaming, and thus module-level pixel reset is
553b4a3d877SJai Luthra  * not asserted.
554b4a3d877SJai Luthra  *
555b4a3d877SJai Luthra  * To prevent that stale data corrupting the subsequent transactions, it is
556b4a3d877SJai Luthra  * required to issue DMA requests to drain it out.
557b4a3d877SJai Luthra  */
ti_csi2rx_drain_dma(struct ti_csi2rx_dev * csi)558b4a3d877SJai Luthra static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
559b4a3d877SJai Luthra {
560b4a3d877SJai Luthra 	struct dma_async_tx_descriptor *desc;
561b4a3d877SJai Luthra 	struct completion drain_complete;
562b4a3d877SJai Luthra 	dma_cookie_t cookie;
563b4a3d877SJai Luthra 	int ret;
564b4a3d877SJai Luthra 
565b4a3d877SJai Luthra 	init_completion(&drain_complete);
566b4a3d877SJai Luthra 
567b4a3d877SJai Luthra 	desc = dmaengine_prep_slave_single(csi->dma.chan, csi->dma.drain.paddr,
568b4a3d877SJai Luthra 					   csi->dma.drain.len, DMA_DEV_TO_MEM,
569b4a3d877SJai Luthra 					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
570b4a3d877SJai Luthra 	if (!desc) {
571b4a3d877SJai Luthra 		ret = -EIO;
572b4a3d877SJai Luthra 		goto out;
573b4a3d877SJai Luthra 	}
574b4a3d877SJai Luthra 
575b4a3d877SJai Luthra 	desc->callback = ti_csi2rx_drain_callback;
576b4a3d877SJai Luthra 	desc->callback_param = &drain_complete;
577b4a3d877SJai Luthra 
578b4a3d877SJai Luthra 	cookie = dmaengine_submit(desc);
579b4a3d877SJai Luthra 	ret = dma_submit_error(cookie);
580b4a3d877SJai Luthra 	if (ret)
581b4a3d877SJai Luthra 		goto out;
582b4a3d877SJai Luthra 
583b4a3d877SJai Luthra 	dma_async_issue_pending(csi->dma.chan);
584b4a3d877SJai Luthra 
585b4a3d877SJai Luthra 	if (!wait_for_completion_timeout(&drain_complete,
586b4a3d877SJai Luthra 					 msecs_to_jiffies(DRAIN_TIMEOUT_MS))) {
587b4a3d877SJai Luthra 		dmaengine_terminate_sync(csi->dma.chan);
588b4a3d877SJai Luthra 		dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n");
589b4a3d877SJai Luthra 		ret = -ETIMEDOUT;
590b4a3d877SJai Luthra 		goto out;
591b4a3d877SJai Luthra 	}
592b4a3d877SJai Luthra out:
593b4a3d877SJai Luthra 	return ret;
594b4a3d877SJai Luthra }
595b4a3d877SJai Luthra 
ti_csi2rx_dma_callback(void * param)596b4a3d877SJai Luthra static void ti_csi2rx_dma_callback(void *param)
597b4a3d877SJai Luthra {
598b4a3d877SJai Luthra 	struct ti_csi2rx_buffer *buf = param;
599b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = buf->csi;
600b4a3d877SJai Luthra 	struct ti_csi2rx_dma *dma = &csi->dma;
601b4a3d877SJai Luthra 	unsigned long flags;
602b4a3d877SJai Luthra 
603b4a3d877SJai Luthra 	/*
604b4a3d877SJai Luthra 	 * TODO: Derive the sequence number from the CSI2RX frame number
605b4a3d877SJai Luthra 	 * hardware monitor registers.
606b4a3d877SJai Luthra 	 */
607b4a3d877SJai Luthra 	buf->vb.vb2_buf.timestamp = ktime_get_ns();
608b4a3d877SJai Luthra 	buf->vb.sequence = csi->sequence++;
609b4a3d877SJai Luthra 
610b4a3d877SJai Luthra 	spin_lock_irqsave(&dma->lock, flags);
611b4a3d877SJai Luthra 
612b4a3d877SJai Luthra 	WARN_ON(!list_is_first(&buf->list, &dma->submitted));
613b4a3d877SJai Luthra 	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
614b4a3d877SJai Luthra 	list_del(&buf->list);
615b4a3d877SJai Luthra 
616b4a3d877SJai Luthra 	/* If there are more buffers to process then start their transfer. */
617b4a3d877SJai Luthra 	while (!list_empty(&dma->queue)) {
618b4a3d877SJai Luthra 		buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
619b4a3d877SJai Luthra 
620b4a3d877SJai Luthra 		if (ti_csi2rx_start_dma(csi, buf)) {
621b4a3d877SJai Luthra 			dev_err(csi->dev, "Failed to queue the next buffer for DMA\n");
622b4a3d877SJai Luthra 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
623b4a3d877SJai Luthra 		} else {
624b4a3d877SJai Luthra 			list_move_tail(&buf->list, &dma->submitted);
625b4a3d877SJai Luthra 		}
626b4a3d877SJai Luthra 	}
627b4a3d877SJai Luthra 
628b4a3d877SJai Luthra 	if (list_empty(&dma->submitted))
629b4a3d877SJai Luthra 		dma->state = TI_CSI2RX_DMA_IDLE;
630b4a3d877SJai Luthra 
631b4a3d877SJai Luthra 	spin_unlock_irqrestore(&dma->lock, flags);
632b4a3d877SJai Luthra }
633b4a3d877SJai Luthra 
ti_csi2rx_start_dma(struct ti_csi2rx_dev * csi,struct ti_csi2rx_buffer * buf)634b4a3d877SJai Luthra static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
635b4a3d877SJai Luthra 			       struct ti_csi2rx_buffer *buf)
636b4a3d877SJai Luthra {
637b4a3d877SJai Luthra 	unsigned long addr;
638b4a3d877SJai Luthra 	struct dma_async_tx_descriptor *desc;
639b4a3d877SJai Luthra 	size_t len = csi->v_fmt.fmt.pix.sizeimage;
640b4a3d877SJai Luthra 	dma_cookie_t cookie;
641b4a3d877SJai Luthra 	int ret = 0;
642b4a3d877SJai Luthra 
643b4a3d877SJai Luthra 	addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
644b4a3d877SJai Luthra 	desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len,
645b4a3d877SJai Luthra 					   DMA_DEV_TO_MEM,
646b4a3d877SJai Luthra 					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
647b4a3d877SJai Luthra 	if (!desc)
648b4a3d877SJai Luthra 		return -EIO;
649b4a3d877SJai Luthra 
650b4a3d877SJai Luthra 	desc->callback = ti_csi2rx_dma_callback;
651b4a3d877SJai Luthra 	desc->callback_param = buf;
652b4a3d877SJai Luthra 
653b4a3d877SJai Luthra 	cookie = dmaengine_submit(desc);
654b4a3d877SJai Luthra 	ret = dma_submit_error(cookie);
655b4a3d877SJai Luthra 	if (ret)
656b4a3d877SJai Luthra 		return ret;
657b4a3d877SJai Luthra 
658b4a3d877SJai Luthra 	dma_async_issue_pending(csi->dma.chan);
659b4a3d877SJai Luthra 
660b4a3d877SJai Luthra 	return 0;
661b4a3d877SJai Luthra }
662b4a3d877SJai Luthra 
ti_csi2rx_stop_dma(struct ti_csi2rx_dev * csi)663b4a3d877SJai Luthra static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi)
664b4a3d877SJai Luthra {
665b4a3d877SJai Luthra 	struct ti_csi2rx_dma *dma = &csi->dma;
666b4a3d877SJai Luthra 	enum ti_csi2rx_dma_state state;
667b4a3d877SJai Luthra 	unsigned long flags;
668b4a3d877SJai Luthra 	int ret;
669b4a3d877SJai Luthra 
670b4a3d877SJai Luthra 	spin_lock_irqsave(&dma->lock, flags);
671b4a3d877SJai Luthra 	state = csi->dma.state;
672b4a3d877SJai Luthra 	dma->state = TI_CSI2RX_DMA_STOPPED;
673b4a3d877SJai Luthra 	spin_unlock_irqrestore(&dma->lock, flags);
674b4a3d877SJai Luthra 
675b4a3d877SJai Luthra 	if (state != TI_CSI2RX_DMA_STOPPED) {
676b4a3d877SJai Luthra 		/*
677b4a3d877SJai Luthra 		 * Normal DMA termination does not clean up pending data on
678b4a3d877SJai Luthra 		 * the endpoint if multiple streams are running and only one
679b4a3d877SJai Luthra 		 * is stopped, as the module-level pixel reset cannot be
680b4a3d877SJai Luthra 		 * enforced before terminating DMA.
681b4a3d877SJai Luthra 		 */
682b4a3d877SJai Luthra 		ret = ti_csi2rx_drain_dma(csi);
683b4a3d877SJai Luthra 		if (ret && ret != -ETIMEDOUT)
684b4a3d877SJai Luthra 			dev_warn(csi->dev,
685b4a3d877SJai Luthra 				 "Failed to drain DMA. Next frame might be bogus\n");
686b4a3d877SJai Luthra 	}
687b4a3d877SJai Luthra 
688b4a3d877SJai Luthra 	ret = dmaengine_terminate_sync(csi->dma.chan);
689b4a3d877SJai Luthra 	if (ret)
690b4a3d877SJai Luthra 		dev_err(csi->dev, "Failed to stop DMA: %d\n", ret);
691b4a3d877SJai Luthra }
692b4a3d877SJai Luthra 
ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev * csi,enum vb2_buffer_state state)693b4a3d877SJai Luthra static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev *csi,
694b4a3d877SJai Luthra 				      enum vb2_buffer_state state)
695b4a3d877SJai Luthra {
696b4a3d877SJai Luthra 	struct ti_csi2rx_dma *dma = &csi->dma;
697b4a3d877SJai Luthra 	struct ti_csi2rx_buffer *buf, *tmp;
698b4a3d877SJai Luthra 	unsigned long flags;
699b4a3d877SJai Luthra 
700b4a3d877SJai Luthra 	spin_lock_irqsave(&dma->lock, flags);
701b4a3d877SJai Luthra 	list_for_each_entry_safe(buf, tmp, &csi->dma.queue, list) {
702b4a3d877SJai Luthra 		list_del(&buf->list);
703b4a3d877SJai Luthra 		vb2_buffer_done(&buf->vb.vb2_buf, state);
704b4a3d877SJai Luthra 	}
705b4a3d877SJai Luthra 	list_for_each_entry_safe(buf, tmp, &csi->dma.submitted, list) {
706b4a3d877SJai Luthra 		list_del(&buf->list);
707b4a3d877SJai Luthra 		vb2_buffer_done(&buf->vb.vb2_buf, state);
708b4a3d877SJai Luthra 	}
709b4a3d877SJai Luthra 	spin_unlock_irqrestore(&dma->lock, flags);
710b4a3d877SJai Luthra }
711b4a3d877SJai Luthra 
ti_csi2rx_queue_setup(struct vb2_queue * q,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])712b4a3d877SJai Luthra static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
713b4a3d877SJai Luthra 				 unsigned int *nplanes, unsigned int sizes[],
714b4a3d877SJai Luthra 				 struct device *alloc_devs[])
715b4a3d877SJai Luthra {
716b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = vb2_get_drv_priv(q);
717b4a3d877SJai Luthra 	unsigned int size = csi->v_fmt.fmt.pix.sizeimage;
718b4a3d877SJai Luthra 
719b4a3d877SJai Luthra 	if (*nplanes) {
720b4a3d877SJai Luthra 		if (sizes[0] < size)
721b4a3d877SJai Luthra 			return -EINVAL;
722b4a3d877SJai Luthra 		size = sizes[0];
723b4a3d877SJai Luthra 	}
724b4a3d877SJai Luthra 
725b4a3d877SJai Luthra 	*nplanes = 1;
726b4a3d877SJai Luthra 	sizes[0] = size;
727b4a3d877SJai Luthra 
728b4a3d877SJai Luthra 	return 0;
729b4a3d877SJai Luthra }
730b4a3d877SJai Luthra 
ti_csi2rx_buffer_prepare(struct vb2_buffer * vb)731b4a3d877SJai Luthra static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb)
732b4a3d877SJai Luthra {
733b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue);
734b4a3d877SJai Luthra 	unsigned long size = csi->v_fmt.fmt.pix.sizeimage;
735b4a3d877SJai Luthra 
736b4a3d877SJai Luthra 	if (vb2_plane_size(vb, 0) < size) {
737b4a3d877SJai Luthra 		dev_err(csi->dev, "Data will not fit into plane\n");
738b4a3d877SJai Luthra 		return -EINVAL;
739b4a3d877SJai Luthra 	}
740b4a3d877SJai Luthra 
741b4a3d877SJai Luthra 	vb2_set_plane_payload(vb, 0, size);
742b4a3d877SJai Luthra 	return 0;
743b4a3d877SJai Luthra }
744b4a3d877SJai Luthra 
ti_csi2rx_buffer_queue(struct vb2_buffer * vb)745b4a3d877SJai Luthra static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
746b4a3d877SJai Luthra {
747b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue);
748b4a3d877SJai Luthra 	struct ti_csi2rx_buffer *buf;
749b4a3d877SJai Luthra 	struct ti_csi2rx_dma *dma = &csi->dma;
750b4a3d877SJai Luthra 	bool restart_dma = false;
751b4a3d877SJai Luthra 	unsigned long flags = 0;
752b4a3d877SJai Luthra 	int ret;
753b4a3d877SJai Luthra 
754b4a3d877SJai Luthra 	buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf);
755b4a3d877SJai Luthra 	buf->csi = csi;
756b4a3d877SJai Luthra 
757b4a3d877SJai Luthra 	spin_lock_irqsave(&dma->lock, flags);
758b4a3d877SJai Luthra 	/*
759b4a3d877SJai Luthra 	 * Usually the DMA callback takes care of queueing the pending buffers.
760b4a3d877SJai Luthra 	 * But if DMA has stalled due to lack of buffers, restart it now.
761b4a3d877SJai Luthra 	 */
762b4a3d877SJai Luthra 	if (dma->state == TI_CSI2RX_DMA_IDLE) {
763b4a3d877SJai Luthra 		/*
764b4a3d877SJai Luthra 		 * Do not restart DMA with the lock held because
765b4a3d877SJai Luthra 		 * ti_csi2rx_drain_dma() might block for completion.
766b4a3d877SJai Luthra 		 * There won't be a race on queueing DMA anyway since the
767b4a3d877SJai Luthra 		 * callback is not being fired.
768b4a3d877SJai Luthra 		 */
769b4a3d877SJai Luthra 		restart_dma = true;
770b4a3d877SJai Luthra 		dma->state = TI_CSI2RX_DMA_ACTIVE;
771b4a3d877SJai Luthra 	} else {
772b4a3d877SJai Luthra 		list_add_tail(&buf->list, &dma->queue);
773b4a3d877SJai Luthra 	}
774b4a3d877SJai Luthra 	spin_unlock_irqrestore(&dma->lock, flags);
775b4a3d877SJai Luthra 
776b4a3d877SJai Luthra 	if (restart_dma) {
777b4a3d877SJai Luthra 		/*
778b4a3d877SJai Luthra 		 * Once frames start dropping, some data gets stuck in the DMA
779b4a3d877SJai Luthra 		 * pipeline somewhere. So the first DMA transfer after frame
780b4a3d877SJai Luthra 		 * drops gives a partial frame. This is obviously not useful to
781b4a3d877SJai Luthra 		 * the application and will only confuse it. Issue a DMA
782b4a3d877SJai Luthra 		 * transaction to drain that up.
783b4a3d877SJai Luthra 		 */
784b4a3d877SJai Luthra 		ret = ti_csi2rx_drain_dma(csi);
785b4a3d877SJai Luthra 		if (ret && ret != -ETIMEDOUT)
786b4a3d877SJai Luthra 			dev_warn(csi->dev,
787b4a3d877SJai Luthra 				 "Failed to drain DMA. Next frame might be bogus\n");
788b4a3d877SJai Luthra 
789*ad79c9ecSJai Luthra 		spin_lock_irqsave(&dma->lock, flags);
790b4a3d877SJai Luthra 		ret = ti_csi2rx_start_dma(csi, buf);
791b4a3d877SJai Luthra 		if (ret) {
792b4a3d877SJai Luthra 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
793b4a3d877SJai Luthra 			dma->state = TI_CSI2RX_DMA_IDLE;
794b4a3d877SJai Luthra 			spin_unlock_irqrestore(&dma->lock, flags);
795*ad79c9ecSJai Luthra 			dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
796b4a3d877SJai Luthra 		} else {
797b4a3d877SJai Luthra 			list_add_tail(&buf->list, &dma->submitted);
798b4a3d877SJai Luthra 			spin_unlock_irqrestore(&dma->lock, flags);
799b4a3d877SJai Luthra 		}
800b4a3d877SJai Luthra 	}
801b4a3d877SJai Luthra }
802b4a3d877SJai Luthra 
ti_csi2rx_start_streaming(struct vb2_queue * vq,unsigned int count)803b4a3d877SJai Luthra static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
804b4a3d877SJai Luthra {
805b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq);
806b4a3d877SJai Luthra 	struct ti_csi2rx_dma *dma = &csi->dma;
807b4a3d877SJai Luthra 	struct ti_csi2rx_buffer *buf;
808b4a3d877SJai Luthra 	unsigned long flags;
809b4a3d877SJai Luthra 	int ret = 0;
810b4a3d877SJai Luthra 
811b4a3d877SJai Luthra 	spin_lock_irqsave(&dma->lock, flags);
812b4a3d877SJai Luthra 	if (list_empty(&dma->queue))
813b4a3d877SJai Luthra 		ret = -EIO;
814b4a3d877SJai Luthra 	spin_unlock_irqrestore(&dma->lock, flags);
815b4a3d877SJai Luthra 	if (ret)
816b4a3d877SJai Luthra 		return ret;
817b4a3d877SJai Luthra 
818b4a3d877SJai Luthra 	ret = video_device_pipeline_start(&csi->vdev, &csi->pipe);
819b4a3d877SJai Luthra 	if (ret)
820b4a3d877SJai Luthra 		goto err;
821b4a3d877SJai Luthra 
822b4a3d877SJai Luthra 	ti_csi2rx_setup_shim(csi);
823b4a3d877SJai Luthra 
824b4a3d877SJai Luthra 	csi->sequence = 0;
825b4a3d877SJai Luthra 
826b4a3d877SJai Luthra 	spin_lock_irqsave(&dma->lock, flags);
827b4a3d877SJai Luthra 	buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
828b4a3d877SJai Luthra 
829b4a3d877SJai Luthra 	ret = ti_csi2rx_start_dma(csi, buf);
830b4a3d877SJai Luthra 	if (ret) {
831b4a3d877SJai Luthra 		dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
832b4a3d877SJai Luthra 		spin_unlock_irqrestore(&dma->lock, flags);
833b4a3d877SJai Luthra 		goto err_pipeline;
834b4a3d877SJai Luthra 	}
835b4a3d877SJai Luthra 
836b4a3d877SJai Luthra 	list_move_tail(&buf->list, &dma->submitted);
837b4a3d877SJai Luthra 	dma->state = TI_CSI2RX_DMA_ACTIVE;
838b4a3d877SJai Luthra 	spin_unlock_irqrestore(&dma->lock, flags);
839b4a3d877SJai Luthra 
840b4a3d877SJai Luthra 	ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
841b4a3d877SJai Luthra 	if (ret)
842b4a3d877SJai Luthra 		goto err_dma;
843b4a3d877SJai Luthra 
844b4a3d877SJai Luthra 	return 0;
845b4a3d877SJai Luthra 
846b4a3d877SJai Luthra err_dma:
847b4a3d877SJai Luthra 	ti_csi2rx_stop_dma(csi);
848b4a3d877SJai Luthra err_pipeline:
849b4a3d877SJai Luthra 	video_device_pipeline_stop(&csi->vdev);
850b4a3d877SJai Luthra 	writel(0, csi->shim + SHIM_CNTL);
851b4a3d877SJai Luthra 	writel(0, csi->shim + SHIM_DMACNTX);
852b4a3d877SJai Luthra err:
853b4a3d877SJai Luthra 	ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED);
854b4a3d877SJai Luthra 	return ret;
855b4a3d877SJai Luthra }
856b4a3d877SJai Luthra 
ti_csi2rx_stop_streaming(struct vb2_queue * vq)857b4a3d877SJai Luthra static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
858b4a3d877SJai Luthra {
859b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq);
860b4a3d877SJai Luthra 	int ret;
861b4a3d877SJai Luthra 
862b4a3d877SJai Luthra 	video_device_pipeline_stop(&csi->vdev);
863b4a3d877SJai Luthra 
864b4a3d877SJai Luthra 	writel(0, csi->shim + SHIM_CNTL);
865b4a3d877SJai Luthra 	writel(0, csi->shim + SHIM_DMACNTX);
866b4a3d877SJai Luthra 
867b4a3d877SJai Luthra 	ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
868b4a3d877SJai Luthra 	if (ret)
869b4a3d877SJai Luthra 		dev_err(csi->dev, "Failed to stop subdev stream\n");
870b4a3d877SJai Luthra 
871b4a3d877SJai Luthra 	ti_csi2rx_stop_dma(csi);
872b4a3d877SJai Luthra 	ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR);
873b4a3d877SJai Luthra }
874b4a3d877SJai Luthra 
875b4a3d877SJai Luthra static const struct vb2_ops csi_vb2_qops = {
876b4a3d877SJai Luthra 	.queue_setup = ti_csi2rx_queue_setup,
877b4a3d877SJai Luthra 	.buf_prepare = ti_csi2rx_buffer_prepare,
878b4a3d877SJai Luthra 	.buf_queue = ti_csi2rx_buffer_queue,
879b4a3d877SJai Luthra 	.start_streaming = ti_csi2rx_start_streaming,
880b4a3d877SJai Luthra 	.stop_streaming = ti_csi2rx_stop_streaming,
881b4a3d877SJai Luthra 	.wait_prepare = vb2_ops_wait_prepare,
882b4a3d877SJai Luthra 	.wait_finish = vb2_ops_wait_finish,
883b4a3d877SJai Luthra };
884b4a3d877SJai Luthra 
ti_csi2rx_init_vb2q(struct ti_csi2rx_dev * csi)885b4a3d877SJai Luthra static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
886b4a3d877SJai Luthra {
887b4a3d877SJai Luthra 	struct vb2_queue *q = &csi->vidq;
888b4a3d877SJai Luthra 	int ret;
889b4a3d877SJai Luthra 
890b4a3d877SJai Luthra 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
891b4a3d877SJai Luthra 	q->io_modes = VB2_MMAP | VB2_DMABUF;
892b4a3d877SJai Luthra 	q->drv_priv = csi;
893b4a3d877SJai Luthra 	q->buf_struct_size = sizeof(struct ti_csi2rx_buffer);
894b4a3d877SJai Luthra 	q->ops = &csi_vb2_qops;
895b4a3d877SJai Luthra 	q->mem_ops = &vb2_dma_contig_memops;
896b4a3d877SJai Luthra 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
897b4a3d877SJai Luthra 	q->dev = dmaengine_get_dma_device(csi->dma.chan);
898b4a3d877SJai Luthra 	q->lock = &csi->mutex;
89980c2b40aSBenjamin Gaignard 	q->min_queued_buffers = 1;
900b4a3d877SJai Luthra 
901b4a3d877SJai Luthra 	ret = vb2_queue_init(q);
902b4a3d877SJai Luthra 	if (ret)
903b4a3d877SJai Luthra 		return ret;
904b4a3d877SJai Luthra 
905b4a3d877SJai Luthra 	csi->vdev.queue = q;
906b4a3d877SJai Luthra 
907b4a3d877SJai Luthra 	return 0;
908b4a3d877SJai Luthra }
909b4a3d877SJai Luthra 
ti_csi2rx_link_validate(struct media_link * link)910b4a3d877SJai Luthra static int ti_csi2rx_link_validate(struct media_link *link)
911b4a3d877SJai Luthra {
912b4a3d877SJai Luthra 	struct media_entity *entity = link->sink->entity;
913b4a3d877SJai Luthra 	struct video_device *vdev = media_entity_to_video_device(entity);
914b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = container_of(vdev, struct ti_csi2rx_dev, vdev);
915b4a3d877SJai Luthra 	struct v4l2_pix_format *csi_fmt = &csi->v_fmt.fmt.pix;
916b4a3d877SJai Luthra 	struct v4l2_subdev_format source_fmt = {
917b4a3d877SJai Luthra 		.which	= V4L2_SUBDEV_FORMAT_ACTIVE,
918b4a3d877SJai Luthra 		.pad	= link->source->index,
919b4a3d877SJai Luthra 	};
920b4a3d877SJai Luthra 	const struct ti_csi2rx_fmt *ti_fmt;
921b4a3d877SJai Luthra 	int ret;
922b4a3d877SJai Luthra 
923b4a3d877SJai Luthra 	ret = v4l2_subdev_call_state_active(csi->source, pad,
924b4a3d877SJai Luthra 					    get_fmt, &source_fmt);
925b4a3d877SJai Luthra 	if (ret)
926b4a3d877SJai Luthra 		return ret;
927b4a3d877SJai Luthra 
928b4a3d877SJai Luthra 	if (source_fmt.format.width != csi_fmt->width) {
929b4a3d877SJai Luthra 		dev_dbg(csi->dev, "Width does not match (source %u, sink %u)\n",
930b4a3d877SJai Luthra 			source_fmt.format.width, csi_fmt->width);
931b4a3d877SJai Luthra 		return -EPIPE;
932b4a3d877SJai Luthra 	}
933b4a3d877SJai Luthra 
934b4a3d877SJai Luthra 	if (source_fmt.format.height != csi_fmt->height) {
935b4a3d877SJai Luthra 		dev_dbg(csi->dev, "Height does not match (source %u, sink %u)\n",
936b4a3d877SJai Luthra 			source_fmt.format.height, csi_fmt->height);
937b4a3d877SJai Luthra 		return -EPIPE;
938b4a3d877SJai Luthra 	}
939b4a3d877SJai Luthra 
940b4a3d877SJai Luthra 	if (source_fmt.format.field != csi_fmt->field &&
941b4a3d877SJai Luthra 	    csi_fmt->field != V4L2_FIELD_NONE) {
942b4a3d877SJai Luthra 		dev_dbg(csi->dev, "Field does not match (source %u, sink %u)\n",
943b4a3d877SJai Luthra 			source_fmt.format.field, csi_fmt->field);
944b4a3d877SJai Luthra 		return -EPIPE;
945b4a3d877SJai Luthra 	}
946b4a3d877SJai Luthra 
947b4a3d877SJai Luthra 	ti_fmt = find_format_by_code(source_fmt.format.code);
948b4a3d877SJai Luthra 	if (!ti_fmt) {
949b4a3d877SJai Luthra 		dev_dbg(csi->dev, "Media bus format 0x%x not supported\n",
950b4a3d877SJai Luthra 			source_fmt.format.code);
951b4a3d877SJai Luthra 		return -EPIPE;
952b4a3d877SJai Luthra 	}
953b4a3d877SJai Luthra 
954b4a3d877SJai Luthra 	if (ti_fmt->fourcc != csi_fmt->pixelformat) {
955b4a3d877SJai Luthra 		dev_dbg(csi->dev,
956b4a3d877SJai Luthra 			"Cannot transform source fmt 0x%x to sink fmt 0x%x\n",
957b4a3d877SJai Luthra 			ti_fmt->fourcc, csi_fmt->pixelformat);
958b4a3d877SJai Luthra 		return -EPIPE;
959b4a3d877SJai Luthra 	}
960b4a3d877SJai Luthra 
961b4a3d877SJai Luthra 	return 0;
962b4a3d877SJai Luthra }
963b4a3d877SJai Luthra 
964b4a3d877SJai Luthra static const struct media_entity_operations ti_csi2rx_video_entity_ops = {
965b4a3d877SJai Luthra 	.link_validate = ti_csi2rx_link_validate,
966b4a3d877SJai Luthra };
967b4a3d877SJai Luthra 
ti_csi2rx_init_dma(struct ti_csi2rx_dev * csi)968b4a3d877SJai Luthra static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi)
969b4a3d877SJai Luthra {
970b4a3d877SJai Luthra 	struct dma_slave_config cfg = {
971b4a3d877SJai Luthra 		.src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES,
972b4a3d877SJai Luthra 	};
973b4a3d877SJai Luthra 	int ret;
974b4a3d877SJai Luthra 
975b4a3d877SJai Luthra 	INIT_LIST_HEAD(&csi->dma.queue);
976b4a3d877SJai Luthra 	INIT_LIST_HEAD(&csi->dma.submitted);
977b4a3d877SJai Luthra 	spin_lock_init(&csi->dma.lock);
978b4a3d877SJai Luthra 
979b4a3d877SJai Luthra 	csi->dma.state = TI_CSI2RX_DMA_STOPPED;
980b4a3d877SJai Luthra 
981b4a3d877SJai Luthra 	csi->dma.chan = dma_request_chan(csi->dev, "rx0");
982b4a3d877SJai Luthra 	if (IS_ERR(csi->dma.chan))
983b4a3d877SJai Luthra 		return PTR_ERR(csi->dma.chan);
984b4a3d877SJai Luthra 
985b4a3d877SJai Luthra 	ret = dmaengine_slave_config(csi->dma.chan, &cfg);
986b4a3d877SJai Luthra 	if (ret) {
987b4a3d877SJai Luthra 		dma_release_channel(csi->dma.chan);
988b4a3d877SJai Luthra 		return ret;
989b4a3d877SJai Luthra 	}
990b4a3d877SJai Luthra 
991b4a3d877SJai Luthra 	csi->dma.drain.len = DRAIN_BUFFER_SIZE;
992b4a3d877SJai Luthra 	csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len,
993b4a3d877SJai Luthra 						  &csi->dma.drain.paddr,
994b4a3d877SJai Luthra 						  GFP_KERNEL);
995b4a3d877SJai Luthra 	if (!csi->dma.drain.vaddr)
996b4a3d877SJai Luthra 		return -ENOMEM;
997b4a3d877SJai Luthra 
998b4a3d877SJai Luthra 	return 0;
999b4a3d877SJai Luthra }
1000b4a3d877SJai Luthra 
ti_csi2rx_v4l2_init(struct ti_csi2rx_dev * csi)1001b4a3d877SJai Luthra static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
1002b4a3d877SJai Luthra {
1003b4a3d877SJai Luthra 	struct media_device *mdev = &csi->mdev;
1004b4a3d877SJai Luthra 	struct video_device *vdev = &csi->vdev;
1005b4a3d877SJai Luthra 	const struct ti_csi2rx_fmt *fmt;
1006b4a3d877SJai Luthra 	struct v4l2_pix_format *pix_fmt = &csi->v_fmt.fmt.pix;
1007b4a3d877SJai Luthra 	int ret;
1008b4a3d877SJai Luthra 
1009b4a3d877SJai Luthra 	fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY);
1010b4a3d877SJai Luthra 	if (!fmt)
1011b4a3d877SJai Luthra 		return -EINVAL;
1012b4a3d877SJai Luthra 
1013b4a3d877SJai Luthra 	pix_fmt->width = 640;
1014b4a3d877SJai Luthra 	pix_fmt->height = 480;
1015b4a3d877SJai Luthra 	pix_fmt->field = V4L2_FIELD_NONE;
1016b4a3d877SJai Luthra 	pix_fmt->colorspace = V4L2_COLORSPACE_SRGB;
1017b4a3d877SJai Luthra 	pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601,
1018b4a3d877SJai Luthra 	pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE,
1019b4a3d877SJai Luthra 	pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB,
1020b4a3d877SJai Luthra 
1021b4a3d877SJai Luthra 	ti_csi2rx_fill_fmt(fmt, &csi->v_fmt);
1022b4a3d877SJai Luthra 
1023b4a3d877SJai Luthra 	mdev->dev = csi->dev;
1024b4a3d877SJai Luthra 	mdev->hw_revision = 1;
1025b4a3d877SJai Luthra 	strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model));
1026b4a3d877SJai Luthra 
1027b4a3d877SJai Luthra 	media_device_init(mdev);
1028b4a3d877SJai Luthra 
1029b4a3d877SJai Luthra 	strscpy(vdev->name, TI_CSI2RX_MODULE_NAME, sizeof(vdev->name));
1030b4a3d877SJai Luthra 	vdev->v4l2_dev = &csi->v4l2_dev;
1031b4a3d877SJai Luthra 	vdev->vfl_dir = VFL_DIR_RX;
1032b4a3d877SJai Luthra 	vdev->fops = &csi_fops;
1033b4a3d877SJai Luthra 	vdev->ioctl_ops = &csi_ioctl_ops;
1034b4a3d877SJai Luthra 	vdev->release = video_device_release_empty;
1035b4a3d877SJai Luthra 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
1036b4a3d877SJai Luthra 			    V4L2_CAP_IO_MC;
1037b4a3d877SJai Luthra 	vdev->lock = &csi->mutex;
1038b4a3d877SJai Luthra 	video_set_drvdata(vdev, csi);
1039b4a3d877SJai Luthra 
1040b4a3d877SJai Luthra 	csi->pad.flags = MEDIA_PAD_FL_SINK;
1041b4a3d877SJai Luthra 	vdev->entity.ops = &ti_csi2rx_video_entity_ops;
1042b4a3d877SJai Luthra 	ret = media_entity_pads_init(&csi->vdev.entity, 1, &csi->pad);
1043b4a3d877SJai Luthra 	if (ret)
1044b4a3d877SJai Luthra 		return ret;
1045b4a3d877SJai Luthra 
1046b4a3d877SJai Luthra 	csi->v4l2_dev.mdev = mdev;
1047b4a3d877SJai Luthra 
1048b4a3d877SJai Luthra 	ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
1049b4a3d877SJai Luthra 	if (ret)
1050b4a3d877SJai Luthra 		return ret;
1051b4a3d877SJai Luthra 
1052b4a3d877SJai Luthra 	ret = media_device_register(mdev);
1053b4a3d877SJai Luthra 	if (ret) {
1054b4a3d877SJai Luthra 		v4l2_device_unregister(&csi->v4l2_dev);
1055b4a3d877SJai Luthra 		media_device_cleanup(mdev);
1056b4a3d877SJai Luthra 		return ret;
1057b4a3d877SJai Luthra 	}
1058b4a3d877SJai Luthra 
1059b4a3d877SJai Luthra 	return 0;
1060b4a3d877SJai Luthra }
1061b4a3d877SJai Luthra 
ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev * csi)1062b4a3d877SJai Luthra static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi)
1063b4a3d877SJai Luthra {
1064b4a3d877SJai Luthra 	dma_free_coherent(csi->dev, csi->dma.drain.len,
1065b4a3d877SJai Luthra 			  csi->dma.drain.vaddr, csi->dma.drain.paddr);
1066b4a3d877SJai Luthra 	csi->dma.drain.vaddr = NULL;
1067b4a3d877SJai Luthra 	dma_release_channel(csi->dma.chan);
1068b4a3d877SJai Luthra }
1069b4a3d877SJai Luthra 
ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev * csi)1070b4a3d877SJai Luthra static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
1071b4a3d877SJai Luthra {
1072b4a3d877SJai Luthra 	media_device_unregister(&csi->mdev);
1073b4a3d877SJai Luthra 	v4l2_device_unregister(&csi->v4l2_dev);
1074b4a3d877SJai Luthra 	media_device_cleanup(&csi->mdev);
1075b4a3d877SJai Luthra }
1076b4a3d877SJai Luthra 
ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev * csi)1077b4a3d877SJai Luthra static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi)
1078b4a3d877SJai Luthra {
1079b4a3d877SJai Luthra 	v4l2_async_nf_unregister(&csi->notifier);
1080b4a3d877SJai Luthra 	v4l2_async_nf_cleanup(&csi->notifier);
1081b4a3d877SJai Luthra }
1082b4a3d877SJai Luthra 
ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev * csi)1083b4a3d877SJai Luthra static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi)
1084b4a3d877SJai Luthra {
1085b4a3d877SJai Luthra 	vb2_queue_release(&csi->vidq);
1086b4a3d877SJai Luthra }
1087b4a3d877SJai Luthra 
ti_csi2rx_probe(struct platform_device * pdev)1088b4a3d877SJai Luthra static int ti_csi2rx_probe(struct platform_device *pdev)
1089b4a3d877SJai Luthra {
1090b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi;
1091b4a3d877SJai Luthra 	int ret;
1092b4a3d877SJai Luthra 
1093b4a3d877SJai Luthra 	csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
1094b4a3d877SJai Luthra 	if (!csi)
1095b4a3d877SJai Luthra 		return -ENOMEM;
1096b4a3d877SJai Luthra 
1097b4a3d877SJai Luthra 	csi->dev = &pdev->dev;
1098b4a3d877SJai Luthra 	platform_set_drvdata(pdev, csi);
1099b4a3d877SJai Luthra 
1100b4a3d877SJai Luthra 	mutex_init(&csi->mutex);
1101619200d5SMarkus Elfring 	csi->shim = devm_platform_ioremap_resource(pdev, 0);
1102b4a3d877SJai Luthra 	if (IS_ERR(csi->shim)) {
1103b4a3d877SJai Luthra 		ret = PTR_ERR(csi->shim);
1104b4a3d877SJai Luthra 		goto err_mutex;
1105b4a3d877SJai Luthra 	}
1106b4a3d877SJai Luthra 
1107b4a3d877SJai Luthra 	ret = ti_csi2rx_init_dma(csi);
1108b4a3d877SJai Luthra 	if (ret)
1109b4a3d877SJai Luthra 		goto err_mutex;
1110b4a3d877SJai Luthra 
1111b4a3d877SJai Luthra 	ret = ti_csi2rx_v4l2_init(csi);
1112b4a3d877SJai Luthra 	if (ret)
1113b4a3d877SJai Luthra 		goto err_dma;
1114b4a3d877SJai Luthra 
1115b4a3d877SJai Luthra 	ret = ti_csi2rx_init_vb2q(csi);
1116b4a3d877SJai Luthra 	if (ret)
1117b4a3d877SJai Luthra 		goto err_v4l2;
1118b4a3d877SJai Luthra 
1119b4a3d877SJai Luthra 	ret = ti_csi2rx_notifier_register(csi);
1120b4a3d877SJai Luthra 	if (ret)
1121b4a3d877SJai Luthra 		goto err_vb2q;
1122b4a3d877SJai Luthra 
1123b4a3d877SJai Luthra 	ret = of_platform_populate(csi->dev->of_node, NULL, NULL, csi->dev);
1124b4a3d877SJai Luthra 	if (ret) {
1125b4a3d877SJai Luthra 		dev_err(csi->dev, "Failed to create children: %d\n", ret);
1126b4a3d877SJai Luthra 		goto err_subdev;
1127b4a3d877SJai Luthra 	}
1128b4a3d877SJai Luthra 
1129b4a3d877SJai Luthra 	return 0;
1130b4a3d877SJai Luthra 
1131b4a3d877SJai Luthra err_subdev:
1132b4a3d877SJai Luthra 	ti_csi2rx_cleanup_subdev(csi);
1133b4a3d877SJai Luthra err_vb2q:
1134b4a3d877SJai Luthra 	ti_csi2rx_cleanup_vb2q(csi);
1135b4a3d877SJai Luthra err_v4l2:
1136b4a3d877SJai Luthra 	ti_csi2rx_cleanup_v4l2(csi);
1137b4a3d877SJai Luthra err_dma:
1138b4a3d877SJai Luthra 	ti_csi2rx_cleanup_dma(csi);
1139b4a3d877SJai Luthra err_mutex:
1140b4a3d877SJai Luthra 	mutex_destroy(&csi->mutex);
1141b4a3d877SJai Luthra 	return ret;
1142b4a3d877SJai Luthra }
1143b4a3d877SJai Luthra 
ti_csi2rx_remove(struct platform_device * pdev)11448796f335SUwe Kleine-König static void ti_csi2rx_remove(struct platform_device *pdev)
1145b4a3d877SJai Luthra {
1146b4a3d877SJai Luthra 	struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev);
1147b4a3d877SJai Luthra 
1148b4a3d877SJai Luthra 	video_unregister_device(&csi->vdev);
1149b4a3d877SJai Luthra 
1150b4a3d877SJai Luthra 	ti_csi2rx_cleanup_vb2q(csi);
1151b4a3d877SJai Luthra 	ti_csi2rx_cleanup_subdev(csi);
1152b4a3d877SJai Luthra 	ti_csi2rx_cleanup_v4l2(csi);
1153b4a3d877SJai Luthra 	ti_csi2rx_cleanup_dma(csi);
1154b4a3d877SJai Luthra 
1155b4a3d877SJai Luthra 	mutex_destroy(&csi->mutex);
1156b4a3d877SJai Luthra }
1157b4a3d877SJai Luthra 
1158b4a3d877SJai Luthra static const struct of_device_id ti_csi2rx_of_match[] = {
1159b4a3d877SJai Luthra 	{ .compatible = "ti,j721e-csi2rx-shim", },
1160b4a3d877SJai Luthra 	{ },
1161b4a3d877SJai Luthra };
1162b4a3d877SJai Luthra MODULE_DEVICE_TABLE(of, ti_csi2rx_of_match);
1163b4a3d877SJai Luthra 
1164b4a3d877SJai Luthra static struct platform_driver ti_csi2rx_pdrv = {
1165b4a3d877SJai Luthra 	.probe = ti_csi2rx_probe,
11668796f335SUwe Kleine-König 	.remove_new = ti_csi2rx_remove,
1167b4a3d877SJai Luthra 	.driver = {
1168b4a3d877SJai Luthra 		.name = TI_CSI2RX_MODULE_NAME,
1169b4a3d877SJai Luthra 		.of_match_table = ti_csi2rx_of_match,
1170b4a3d877SJai Luthra 	},
1171b4a3d877SJai Luthra };
1172b4a3d877SJai Luthra 
1173b4a3d877SJai Luthra module_platform_driver(ti_csi2rx_pdrv);
1174b4a3d877SJai Luthra 
1175b4a3d877SJai Luthra MODULE_DESCRIPTION("TI J721E CSI2 RX Driver");
1176b4a3d877SJai Luthra MODULE_AUTHOR("Jai Luthra <j-luthra@ti.com>");
1177b4a3d877SJai Luthra MODULE_LICENSE("GPL");
1178