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