xref: /openbmc/linux/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1577bbf23SMaxime Ripard // SPDX-License-Identifier: GPL-2.0+
2577bbf23SMaxime Ripard /*
3577bbf23SMaxime Ripard  * Copyright (C) 2016 NextThing Co
4577bbf23SMaxime Ripard  * Copyright (C) 2016-2019 Bootlin
5577bbf23SMaxime Ripard  *
6577bbf23SMaxime Ripard  * Author: Maxime Ripard <maxime.ripard@bootlin.com>
7577bbf23SMaxime Ripard  */
8577bbf23SMaxime Ripard 
9577bbf23SMaxime Ripard #include <linux/device.h>
10577bbf23SMaxime Ripard #include <linux/interrupt.h>
11577bbf23SMaxime Ripard #include <linux/list.h>
12577bbf23SMaxime Ripard #include <linux/mutex.h>
13577bbf23SMaxime Ripard #include <linux/spinlock.h>
14577bbf23SMaxime Ripard #include <media/videobuf2-dma-contig.h>
15577bbf23SMaxime Ripard #include <media/videobuf2-v4l2.h>
16577bbf23SMaxime Ripard 
17577bbf23SMaxime Ripard #include "sun4i_csi.h"
18577bbf23SMaxime Ripard 
19577bbf23SMaxime Ripard struct sun4i_csi_buffer {
20577bbf23SMaxime Ripard 	struct vb2_v4l2_buffer	vb;
21577bbf23SMaxime Ripard 	struct list_head	list;
22577bbf23SMaxime Ripard };
23577bbf23SMaxime Ripard 
24577bbf23SMaxime Ripard static inline struct sun4i_csi_buffer *
vb2_v4l2_to_csi_buffer(const struct vb2_v4l2_buffer * p)25577bbf23SMaxime Ripard vb2_v4l2_to_csi_buffer(const struct vb2_v4l2_buffer *p)
26577bbf23SMaxime Ripard {
27577bbf23SMaxime Ripard 	return container_of(p, struct sun4i_csi_buffer, vb);
28577bbf23SMaxime Ripard }
29577bbf23SMaxime Ripard 
30577bbf23SMaxime Ripard static inline struct sun4i_csi_buffer *
vb2_to_csi_buffer(const struct vb2_buffer * p)31577bbf23SMaxime Ripard vb2_to_csi_buffer(const struct vb2_buffer *p)
32577bbf23SMaxime Ripard {
33577bbf23SMaxime Ripard 	return vb2_v4l2_to_csi_buffer(to_vb2_v4l2_buffer(p));
34577bbf23SMaxime Ripard }
35577bbf23SMaxime Ripard 
sun4i_csi_capture_start(struct sun4i_csi * csi)36577bbf23SMaxime Ripard static void sun4i_csi_capture_start(struct sun4i_csi *csi)
37577bbf23SMaxime Ripard {
38577bbf23SMaxime Ripard 	writel(CSI_CPT_CTRL_VIDEO_START, csi->regs + CSI_CPT_CTRL_REG);
39577bbf23SMaxime Ripard }
40577bbf23SMaxime Ripard 
sun4i_csi_capture_stop(struct sun4i_csi * csi)41577bbf23SMaxime Ripard static void sun4i_csi_capture_stop(struct sun4i_csi *csi)
42577bbf23SMaxime Ripard {
43577bbf23SMaxime Ripard 	writel(0, csi->regs + CSI_CPT_CTRL_REG);
44577bbf23SMaxime Ripard }
45577bbf23SMaxime Ripard 
sun4i_csi_queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])46577bbf23SMaxime Ripard static int sun4i_csi_queue_setup(struct vb2_queue *vq,
47577bbf23SMaxime Ripard 				 unsigned int *nbuffers,
48577bbf23SMaxime Ripard 				 unsigned int *nplanes,
49577bbf23SMaxime Ripard 				 unsigned int sizes[],
50577bbf23SMaxime Ripard 				 struct device *alloc_devs[])
51577bbf23SMaxime Ripard {
52577bbf23SMaxime Ripard 	struct sun4i_csi *csi = vb2_get_drv_priv(vq);
53577bbf23SMaxime Ripard 	unsigned int num_planes = csi->fmt.num_planes;
54577bbf23SMaxime Ripard 	unsigned int i;
55577bbf23SMaxime Ripard 
56577bbf23SMaxime Ripard 	if (*nplanes) {
57577bbf23SMaxime Ripard 		if (*nplanes != num_planes)
58577bbf23SMaxime Ripard 			return -EINVAL;
59577bbf23SMaxime Ripard 
60577bbf23SMaxime Ripard 		for (i = 0; i < num_planes; i++)
61577bbf23SMaxime Ripard 			if (sizes[i] < csi->fmt.plane_fmt[i].sizeimage)
62577bbf23SMaxime Ripard 				return -EINVAL;
63577bbf23SMaxime Ripard 		return 0;
64577bbf23SMaxime Ripard 	}
65577bbf23SMaxime Ripard 
66577bbf23SMaxime Ripard 	*nplanes = num_planes;
67577bbf23SMaxime Ripard 	for (i = 0; i < num_planes; i++)
68577bbf23SMaxime Ripard 		sizes[i] = csi->fmt.plane_fmt[i].sizeimage;
69577bbf23SMaxime Ripard 
70577bbf23SMaxime Ripard 	return 0;
71577bbf23SMaxime Ripard };
72577bbf23SMaxime Ripard 
sun4i_csi_buffer_prepare(struct vb2_buffer * vb)73577bbf23SMaxime Ripard static int sun4i_csi_buffer_prepare(struct vb2_buffer *vb)
74577bbf23SMaxime Ripard {
75577bbf23SMaxime Ripard 	struct sun4i_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
76577bbf23SMaxime Ripard 	unsigned int i;
77577bbf23SMaxime Ripard 
78577bbf23SMaxime Ripard 	for (i = 0; i < csi->fmt.num_planes; i++) {
79577bbf23SMaxime Ripard 		unsigned long size = csi->fmt.plane_fmt[i].sizeimage;
80577bbf23SMaxime Ripard 
81577bbf23SMaxime Ripard 		if (vb2_plane_size(vb, i) < size) {
82577bbf23SMaxime Ripard 			dev_err(csi->dev, "buffer too small (%lu < %lu)\n",
83577bbf23SMaxime Ripard 				vb2_plane_size(vb, i), size);
84577bbf23SMaxime Ripard 			return -EINVAL;
85577bbf23SMaxime Ripard 		}
86577bbf23SMaxime Ripard 
87577bbf23SMaxime Ripard 		vb2_set_plane_payload(vb, i, size);
88577bbf23SMaxime Ripard 	}
89577bbf23SMaxime Ripard 
90577bbf23SMaxime Ripard 	return 0;
91577bbf23SMaxime Ripard }
92577bbf23SMaxime Ripard 
sun4i_csi_setup_scratch_buffer(struct sun4i_csi * csi,unsigned int slot)93577bbf23SMaxime Ripard static int sun4i_csi_setup_scratch_buffer(struct sun4i_csi *csi,
94577bbf23SMaxime Ripard 					  unsigned int slot)
95577bbf23SMaxime Ripard {
96577bbf23SMaxime Ripard 	dma_addr_t addr = csi->scratch.paddr;
97577bbf23SMaxime Ripard 	unsigned int plane;
98577bbf23SMaxime Ripard 
99577bbf23SMaxime Ripard 	dev_dbg(csi->dev,
100577bbf23SMaxime Ripard 		"No more available buffer, using the scratch buffer\n");
101577bbf23SMaxime Ripard 
102577bbf23SMaxime Ripard 	for (plane = 0; plane < csi->fmt.num_planes; plane++) {
103577bbf23SMaxime Ripard 		writel(addr, csi->regs + CSI_BUF_ADDR_REG(plane, slot));
104577bbf23SMaxime Ripard 		addr += csi->fmt.plane_fmt[plane].sizeimage;
105577bbf23SMaxime Ripard 	}
106577bbf23SMaxime Ripard 
107577bbf23SMaxime Ripard 	csi->current_buf[slot] = NULL;
108577bbf23SMaxime Ripard 	return 0;
109577bbf23SMaxime Ripard }
110577bbf23SMaxime Ripard 
sun4i_csi_buffer_fill_slot(struct sun4i_csi * csi,unsigned int slot)111577bbf23SMaxime Ripard static int sun4i_csi_buffer_fill_slot(struct sun4i_csi *csi, unsigned int slot)
112577bbf23SMaxime Ripard {
113577bbf23SMaxime Ripard 	struct sun4i_csi_buffer *c_buf;
114577bbf23SMaxime Ripard 	struct vb2_v4l2_buffer *v_buf;
115577bbf23SMaxime Ripard 	unsigned int plane;
116577bbf23SMaxime Ripard 
117577bbf23SMaxime Ripard 	/*
118577bbf23SMaxime Ripard 	 * We should never end up in a situation where we overwrite an
119577bbf23SMaxime Ripard 	 * already filled slot.
120577bbf23SMaxime Ripard 	 */
121577bbf23SMaxime Ripard 	if (WARN_ON(csi->current_buf[slot]))
122577bbf23SMaxime Ripard 		return -EINVAL;
123577bbf23SMaxime Ripard 
124577bbf23SMaxime Ripard 	if (list_empty(&csi->buf_list))
125577bbf23SMaxime Ripard 		return sun4i_csi_setup_scratch_buffer(csi, slot);
126577bbf23SMaxime Ripard 
127577bbf23SMaxime Ripard 	c_buf = list_first_entry(&csi->buf_list, struct sun4i_csi_buffer, list);
128577bbf23SMaxime Ripard 	list_del_init(&c_buf->list);
129577bbf23SMaxime Ripard 
130577bbf23SMaxime Ripard 	v_buf = &c_buf->vb;
131577bbf23SMaxime Ripard 	csi->current_buf[slot] = v_buf;
132577bbf23SMaxime Ripard 
133577bbf23SMaxime Ripard 	for (plane = 0; plane < csi->fmt.num_planes; plane++) {
134577bbf23SMaxime Ripard 		dma_addr_t buf_addr;
135577bbf23SMaxime Ripard 
136577bbf23SMaxime Ripard 		buf_addr = vb2_dma_contig_plane_dma_addr(&v_buf->vb2_buf,
137577bbf23SMaxime Ripard 							 plane);
138577bbf23SMaxime Ripard 		writel(buf_addr, csi->regs + CSI_BUF_ADDR_REG(plane, slot));
139577bbf23SMaxime Ripard 	}
140577bbf23SMaxime Ripard 
141577bbf23SMaxime Ripard 	return 0;
142577bbf23SMaxime Ripard }
143577bbf23SMaxime Ripard 
sun4i_csi_buffer_fill_all(struct sun4i_csi * csi)144577bbf23SMaxime Ripard static int sun4i_csi_buffer_fill_all(struct sun4i_csi *csi)
145577bbf23SMaxime Ripard {
146577bbf23SMaxime Ripard 	unsigned int slot;
147577bbf23SMaxime Ripard 	int ret;
148577bbf23SMaxime Ripard 
149577bbf23SMaxime Ripard 	for (slot = 0; slot < CSI_MAX_BUFFER; slot++) {
150577bbf23SMaxime Ripard 		ret = sun4i_csi_buffer_fill_slot(csi, slot);
151577bbf23SMaxime Ripard 		if (ret)
152577bbf23SMaxime Ripard 			return ret;
153577bbf23SMaxime Ripard 	}
154577bbf23SMaxime Ripard 
155577bbf23SMaxime Ripard 	return 0;
156577bbf23SMaxime Ripard }
157577bbf23SMaxime Ripard 
sun4i_csi_buffer_mark_done(struct sun4i_csi * csi,unsigned int slot,unsigned int sequence)158577bbf23SMaxime Ripard static void sun4i_csi_buffer_mark_done(struct sun4i_csi *csi,
159577bbf23SMaxime Ripard 				       unsigned int slot,
160577bbf23SMaxime Ripard 				       unsigned int sequence)
161577bbf23SMaxime Ripard {
162577bbf23SMaxime Ripard 	struct vb2_v4l2_buffer *v_buf;
163577bbf23SMaxime Ripard 
164577bbf23SMaxime Ripard 	if (!csi->current_buf[slot]) {
165577bbf23SMaxime Ripard 		dev_dbg(csi->dev, "Scratch buffer was used, ignoring..\n");
166577bbf23SMaxime Ripard 		return;
167577bbf23SMaxime Ripard 	}
168577bbf23SMaxime Ripard 
169577bbf23SMaxime Ripard 	v_buf = csi->current_buf[slot];
170577bbf23SMaxime Ripard 	v_buf->field = csi->fmt.field;
171577bbf23SMaxime Ripard 	v_buf->sequence = sequence;
172577bbf23SMaxime Ripard 	v_buf->vb2_buf.timestamp = ktime_get_ns();
173577bbf23SMaxime Ripard 	vb2_buffer_done(&v_buf->vb2_buf, VB2_BUF_STATE_DONE);
174577bbf23SMaxime Ripard 
175577bbf23SMaxime Ripard 	csi->current_buf[slot] = NULL;
176577bbf23SMaxime Ripard }
177577bbf23SMaxime Ripard 
sun4i_csi_buffer_flip(struct sun4i_csi * csi,unsigned int sequence)178577bbf23SMaxime Ripard static int sun4i_csi_buffer_flip(struct sun4i_csi *csi, unsigned int sequence)
179577bbf23SMaxime Ripard {
180577bbf23SMaxime Ripard 	u32 reg = readl(csi->regs + CSI_BUF_CTRL_REG);
181577bbf23SMaxime Ripard 	unsigned int next;
182577bbf23SMaxime Ripard 
183577bbf23SMaxime Ripard 	/* Our next buffer is not the current buffer */
184577bbf23SMaxime Ripard 	next = !(reg & CSI_BUF_CTRL_DBS);
185577bbf23SMaxime Ripard 
186577bbf23SMaxime Ripard 	/* Report the previous buffer as done */
187577bbf23SMaxime Ripard 	sun4i_csi_buffer_mark_done(csi, next, sequence);
188577bbf23SMaxime Ripard 
189577bbf23SMaxime Ripard 	/* Put a new buffer in there */
190577bbf23SMaxime Ripard 	return sun4i_csi_buffer_fill_slot(csi, next);
191577bbf23SMaxime Ripard }
192577bbf23SMaxime Ripard 
sun4i_csi_buffer_queue(struct vb2_buffer * vb)193577bbf23SMaxime Ripard static void sun4i_csi_buffer_queue(struct vb2_buffer *vb)
194577bbf23SMaxime Ripard {
195577bbf23SMaxime Ripard 	struct sun4i_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
196577bbf23SMaxime Ripard 	struct sun4i_csi_buffer *buf = vb2_to_csi_buffer(vb);
197577bbf23SMaxime Ripard 	unsigned long flags;
198577bbf23SMaxime Ripard 
199577bbf23SMaxime Ripard 	spin_lock_irqsave(&csi->qlock, flags);
200577bbf23SMaxime Ripard 	list_add_tail(&buf->list, &csi->buf_list);
201577bbf23SMaxime Ripard 	spin_unlock_irqrestore(&csi->qlock, flags);
202577bbf23SMaxime Ripard }
203577bbf23SMaxime Ripard 
return_all_buffers(struct sun4i_csi * csi,enum vb2_buffer_state state)204577bbf23SMaxime Ripard static void return_all_buffers(struct sun4i_csi *csi,
205577bbf23SMaxime Ripard 			       enum vb2_buffer_state state)
206577bbf23SMaxime Ripard {
207577bbf23SMaxime Ripard 	struct sun4i_csi_buffer *buf, *node;
208577bbf23SMaxime Ripard 	unsigned int slot;
209577bbf23SMaxime Ripard 
210577bbf23SMaxime Ripard 	list_for_each_entry_safe(buf, node, &csi->buf_list, list) {
211577bbf23SMaxime Ripard 		vb2_buffer_done(&buf->vb.vb2_buf, state);
212577bbf23SMaxime Ripard 		list_del(&buf->list);
213577bbf23SMaxime Ripard 	}
214577bbf23SMaxime Ripard 
215577bbf23SMaxime Ripard 	for (slot = 0; slot < CSI_MAX_BUFFER; slot++) {
216577bbf23SMaxime Ripard 		struct vb2_v4l2_buffer *v_buf = csi->current_buf[slot];
217577bbf23SMaxime Ripard 
218577bbf23SMaxime Ripard 		if (!v_buf)
219577bbf23SMaxime Ripard 			continue;
220577bbf23SMaxime Ripard 
221577bbf23SMaxime Ripard 		vb2_buffer_done(&v_buf->vb2_buf, state);
222577bbf23SMaxime Ripard 		csi->current_buf[slot] = NULL;
223577bbf23SMaxime Ripard 	}
224577bbf23SMaxime Ripard }
225577bbf23SMaxime Ripard 
sun4i_csi_start_streaming(struct vb2_queue * vq,unsigned int count)226577bbf23SMaxime Ripard static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
227577bbf23SMaxime Ripard {
228577bbf23SMaxime Ripard 	struct sun4i_csi *csi = vb2_get_drv_priv(vq);
22994d964e5SLaurent Pinchart 	struct v4l2_mbus_config_parallel *bus = &csi->bus;
230577bbf23SMaxime Ripard 	const struct sun4i_csi_format *csi_fmt;
2311948dcf0SChen-Yu Tsai 	unsigned long href_pol, pclk_pol, vref_pol;
232577bbf23SMaxime Ripard 	unsigned long flags;
233577bbf23SMaxime Ripard 	unsigned int i;
234577bbf23SMaxime Ripard 	int ret;
235577bbf23SMaxime Ripard 
236577bbf23SMaxime Ripard 	csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL);
237577bbf23SMaxime Ripard 	if (!csi_fmt)
238577bbf23SMaxime Ripard 		return -EINVAL;
239577bbf23SMaxime Ripard 
240577bbf23SMaxime Ripard 	dev_dbg(csi->dev, "Starting capture\n");
241577bbf23SMaxime Ripard 
242577bbf23SMaxime Ripard 	csi->sequence = 0;
243577bbf23SMaxime Ripard 
244577bbf23SMaxime Ripard 	/*
245577bbf23SMaxime Ripard 	 * We need a scratch buffer in case where we'll not have any
246577bbf23SMaxime Ripard 	 * more buffer queued so that we don't error out. One of those
247577bbf23SMaxime Ripard 	 * cases is when you end up at the last frame to capture, you
248*c699ce1aSOleg Verych 	 * don't have any buffer queued any more, and yet it doesn't
249577bbf23SMaxime Ripard 	 * really matter since you'll never reach the next buffer.
250577bbf23SMaxime Ripard 	 *
251577bbf23SMaxime Ripard 	 * Since we support the multi-planar API, we need to have a
252577bbf23SMaxime Ripard 	 * buffer for each plane. Allocating a single one large enough
253577bbf23SMaxime Ripard 	 * to hold all the buffers is simpler, so let's go for that.
254577bbf23SMaxime Ripard 	 */
255577bbf23SMaxime Ripard 	csi->scratch.size = 0;
256577bbf23SMaxime Ripard 	for (i = 0; i < csi->fmt.num_planes; i++)
257577bbf23SMaxime Ripard 		csi->scratch.size += csi->fmt.plane_fmt[i].sizeimage;
258577bbf23SMaxime Ripard 
259577bbf23SMaxime Ripard 	csi->scratch.vaddr = dma_alloc_coherent(csi->dev,
260577bbf23SMaxime Ripard 						csi->scratch.size,
261577bbf23SMaxime Ripard 						&csi->scratch.paddr,
262577bbf23SMaxime Ripard 						GFP_KERNEL);
263577bbf23SMaxime Ripard 	if (!csi->scratch.vaddr) {
264577bbf23SMaxime Ripard 		dev_err(csi->dev, "Failed to allocate scratch buffer\n");
265577bbf23SMaxime Ripard 		ret = -ENOMEM;
266577bbf23SMaxime Ripard 		goto err_clear_dma_queue;
267577bbf23SMaxime Ripard 	}
268577bbf23SMaxime Ripard 
2696eaff06aSTomi Valkeinen 	ret = video_device_pipeline_alloc_start(&csi->vdev);
270577bbf23SMaxime Ripard 	if (ret < 0)
271577bbf23SMaxime Ripard 		goto err_free_scratch_buffer;
272577bbf23SMaxime Ripard 
273577bbf23SMaxime Ripard 	spin_lock_irqsave(&csi->qlock, flags);
274577bbf23SMaxime Ripard 
275577bbf23SMaxime Ripard 	/* Setup timings */
276577bbf23SMaxime Ripard 	writel(CSI_WIN_CTRL_W_ACTIVE(csi->fmt.width * 2),
277577bbf23SMaxime Ripard 	       csi->regs + CSI_WIN_CTRL_W_REG);
278577bbf23SMaxime Ripard 	writel(CSI_WIN_CTRL_H_ACTIVE(csi->fmt.height),
279577bbf23SMaxime Ripard 	       csi->regs + CSI_WIN_CTRL_H_REG);
280577bbf23SMaxime Ripard 
2811948dcf0SChen-Yu Tsai 	/*
2821948dcf0SChen-Yu Tsai 	 * This hardware uses [HV]REF instead of [HV]SYNC. Based on the
2831948dcf0SChen-Yu Tsai 	 * provided timing diagrams in the manual, positive polarity
2841948dcf0SChen-Yu Tsai 	 * equals active high [HV]REF.
2851948dcf0SChen-Yu Tsai 	 *
2861948dcf0SChen-Yu Tsai 	 * When the back porch is 0, [HV]REF is more or less equivalent
2871948dcf0SChen-Yu Tsai 	 * to [HV]SYNC inverted.
2881948dcf0SChen-Yu Tsai 	 */
2891948dcf0SChen-Yu Tsai 	href_pol = !!(bus->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW);
2901948dcf0SChen-Yu Tsai 	vref_pol = !!(bus->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW);
291cf9e6d5dSChen-Yu Tsai 	pclk_pol = !!(bus->flags & V4L2_MBUS_PCLK_SAMPLE_RISING);
292577bbf23SMaxime Ripard 	writel(CSI_CFG_INPUT_FMT(csi_fmt->input) |
293577bbf23SMaxime Ripard 	       CSI_CFG_OUTPUT_FMT(csi_fmt->output) |
2941948dcf0SChen-Yu Tsai 	       CSI_CFG_VREF_POL(vref_pol) |
2951948dcf0SChen-Yu Tsai 	       CSI_CFG_HREF_POL(href_pol) |
296577bbf23SMaxime Ripard 	       CSI_CFG_PCLK_POL(pclk_pol),
297577bbf23SMaxime Ripard 	       csi->regs + CSI_CFG_REG);
298577bbf23SMaxime Ripard 
299577bbf23SMaxime Ripard 	/* Setup buffer length */
300577bbf23SMaxime Ripard 	writel(csi->fmt.plane_fmt[0].bytesperline,
301577bbf23SMaxime Ripard 	       csi->regs + CSI_BUF_LEN_REG);
302577bbf23SMaxime Ripard 
303577bbf23SMaxime Ripard 	/* Prepare our buffers in hardware */
304577bbf23SMaxime Ripard 	ret = sun4i_csi_buffer_fill_all(csi);
305577bbf23SMaxime Ripard 	if (ret) {
306577bbf23SMaxime Ripard 		spin_unlock_irqrestore(&csi->qlock, flags);
307577bbf23SMaxime Ripard 		goto err_disable_pipeline;
308577bbf23SMaxime Ripard 	}
309577bbf23SMaxime Ripard 
310577bbf23SMaxime Ripard 	/* Enable double buffering */
311577bbf23SMaxime Ripard 	writel(CSI_BUF_CTRL_DBE, csi->regs + CSI_BUF_CTRL_REG);
312577bbf23SMaxime Ripard 
313577bbf23SMaxime Ripard 	/* Clear the pending interrupts */
314*c699ce1aSOleg Verych 	writel(CSI_INT_FRM_DONE, csi->regs + CSI_INT_STA_REG);
315577bbf23SMaxime Ripard 
316577bbf23SMaxime Ripard 	/* Enable frame done interrupt */
317577bbf23SMaxime Ripard 	writel(CSI_INT_FRM_DONE, csi->regs + CSI_INT_EN_REG);
318577bbf23SMaxime Ripard 
319577bbf23SMaxime Ripard 	sun4i_csi_capture_start(csi);
320577bbf23SMaxime Ripard 
321577bbf23SMaxime Ripard 	spin_unlock_irqrestore(&csi->qlock, flags);
322577bbf23SMaxime Ripard 
323577bbf23SMaxime Ripard 	ret = v4l2_subdev_call(csi->src_subdev, video, s_stream, 1);
324577bbf23SMaxime Ripard 	if (ret < 0 && ret != -ENOIOCTLCMD)
325577bbf23SMaxime Ripard 		goto err_disable_device;
326577bbf23SMaxime Ripard 
327577bbf23SMaxime Ripard 	return 0;
328577bbf23SMaxime Ripard 
329577bbf23SMaxime Ripard err_disable_device:
330577bbf23SMaxime Ripard 	sun4i_csi_capture_stop(csi);
331577bbf23SMaxime Ripard 
332577bbf23SMaxime Ripard err_disable_pipeline:
33312cecbf9STomi Valkeinen 	video_device_pipeline_stop(&csi->vdev);
334577bbf23SMaxime Ripard 
335577bbf23SMaxime Ripard err_free_scratch_buffer:
336577bbf23SMaxime Ripard 	dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
337577bbf23SMaxime Ripard 			  csi->scratch.paddr);
338577bbf23SMaxime Ripard 
339577bbf23SMaxime Ripard err_clear_dma_queue:
340577bbf23SMaxime Ripard 	spin_lock_irqsave(&csi->qlock, flags);
341577bbf23SMaxime Ripard 	return_all_buffers(csi, VB2_BUF_STATE_QUEUED);
342577bbf23SMaxime Ripard 	spin_unlock_irqrestore(&csi->qlock, flags);
343577bbf23SMaxime Ripard 
344577bbf23SMaxime Ripard 	return ret;
345577bbf23SMaxime Ripard }
346577bbf23SMaxime Ripard 
sun4i_csi_stop_streaming(struct vb2_queue * vq)347577bbf23SMaxime Ripard static void sun4i_csi_stop_streaming(struct vb2_queue *vq)
348577bbf23SMaxime Ripard {
349577bbf23SMaxime Ripard 	struct sun4i_csi *csi = vb2_get_drv_priv(vq);
350577bbf23SMaxime Ripard 	unsigned long flags;
351577bbf23SMaxime Ripard 
352577bbf23SMaxime Ripard 	dev_dbg(csi->dev, "Stopping capture\n");
353577bbf23SMaxime Ripard 
354577bbf23SMaxime Ripard 	v4l2_subdev_call(csi->src_subdev, video, s_stream, 0);
355577bbf23SMaxime Ripard 	sun4i_csi_capture_stop(csi);
356577bbf23SMaxime Ripard 
357577bbf23SMaxime Ripard 	/* Release all active buffers */
358577bbf23SMaxime Ripard 	spin_lock_irqsave(&csi->qlock, flags);
359577bbf23SMaxime Ripard 	return_all_buffers(csi, VB2_BUF_STATE_ERROR);
360577bbf23SMaxime Ripard 	spin_unlock_irqrestore(&csi->qlock, flags);
361577bbf23SMaxime Ripard 
36212cecbf9STomi Valkeinen 	video_device_pipeline_stop(&csi->vdev);
363577bbf23SMaxime Ripard 
364577bbf23SMaxime Ripard 	dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
365577bbf23SMaxime Ripard 			  csi->scratch.paddr);
366577bbf23SMaxime Ripard }
367577bbf23SMaxime Ripard 
368577bbf23SMaxime Ripard static const struct vb2_ops sun4i_csi_qops = {
369577bbf23SMaxime Ripard 	.queue_setup		= sun4i_csi_queue_setup,
370577bbf23SMaxime Ripard 	.buf_prepare		= sun4i_csi_buffer_prepare,
371577bbf23SMaxime Ripard 	.buf_queue		= sun4i_csi_buffer_queue,
372577bbf23SMaxime Ripard 	.start_streaming	= sun4i_csi_start_streaming,
373577bbf23SMaxime Ripard 	.stop_streaming		= sun4i_csi_stop_streaming,
374577bbf23SMaxime Ripard 	.wait_prepare		= vb2_ops_wait_prepare,
375577bbf23SMaxime Ripard 	.wait_finish		= vb2_ops_wait_finish,
376577bbf23SMaxime Ripard };
377577bbf23SMaxime Ripard 
sun4i_csi_irq(int irq,void * data)378577bbf23SMaxime Ripard static irqreturn_t sun4i_csi_irq(int irq, void *data)
379577bbf23SMaxime Ripard {
380577bbf23SMaxime Ripard 	struct sun4i_csi *csi = data;
381577bbf23SMaxime Ripard 	u32 reg;
382577bbf23SMaxime Ripard 
383577bbf23SMaxime Ripard 	reg = readl(csi->regs + CSI_INT_STA_REG);
384577bbf23SMaxime Ripard 
385577bbf23SMaxime Ripard 	/* Acknowledge the interrupts */
386577bbf23SMaxime Ripard 	writel(reg, csi->regs + CSI_INT_STA_REG);
387577bbf23SMaxime Ripard 
388577bbf23SMaxime Ripard 	if (!(reg & CSI_INT_FRM_DONE))
389577bbf23SMaxime Ripard 		return IRQ_HANDLED;
390577bbf23SMaxime Ripard 
391577bbf23SMaxime Ripard 	spin_lock(&csi->qlock);
392577bbf23SMaxime Ripard 	if (sun4i_csi_buffer_flip(csi, csi->sequence++)) {
393577bbf23SMaxime Ripard 		dev_warn(csi->dev, "%s: Flip failed\n", __func__);
394577bbf23SMaxime Ripard 		sun4i_csi_capture_stop(csi);
395577bbf23SMaxime Ripard 	}
396577bbf23SMaxime Ripard 	spin_unlock(&csi->qlock);
397577bbf23SMaxime Ripard 
398577bbf23SMaxime Ripard 	return IRQ_HANDLED;
399577bbf23SMaxime Ripard }
400577bbf23SMaxime Ripard 
sun4i_csi_dma_register(struct sun4i_csi * csi,int irq)401577bbf23SMaxime Ripard int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq)
402577bbf23SMaxime Ripard {
403577bbf23SMaxime Ripard 	struct vb2_queue *q = &csi->queue;
404577bbf23SMaxime Ripard 	int ret;
405577bbf23SMaxime Ripard 	int i;
406577bbf23SMaxime Ripard 
407577bbf23SMaxime Ripard 	spin_lock_init(&csi->qlock);
408577bbf23SMaxime Ripard 	mutex_init(&csi->lock);
409577bbf23SMaxime Ripard 
410577bbf23SMaxime Ripard 	INIT_LIST_HEAD(&csi->buf_list);
411577bbf23SMaxime Ripard 	for (i = 0; i < CSI_MAX_BUFFER; i++)
412577bbf23SMaxime Ripard 		csi->current_buf[i] = NULL;
413577bbf23SMaxime Ripard 
414577bbf23SMaxime Ripard 	q->min_buffers_needed = 3;
415577bbf23SMaxime Ripard 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
41671ecc535SHans Verkuil 	q->io_modes = VB2_MMAP | VB2_DMABUF;
417577bbf23SMaxime Ripard 	q->lock = &csi->lock;
418577bbf23SMaxime Ripard 	q->drv_priv = csi;
419577bbf23SMaxime Ripard 	q->buf_struct_size = sizeof(struct sun4i_csi_buffer);
420577bbf23SMaxime Ripard 	q->ops = &sun4i_csi_qops;
421577bbf23SMaxime Ripard 	q->mem_ops = &vb2_dma_contig_memops;
422577bbf23SMaxime Ripard 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
423577bbf23SMaxime Ripard 	q->dev = csi->dev;
424577bbf23SMaxime Ripard 
425577bbf23SMaxime Ripard 	ret = vb2_queue_init(q);
426577bbf23SMaxime Ripard 	if (ret < 0) {
427577bbf23SMaxime Ripard 		dev_err(csi->dev, "failed to initialize VB2 queue\n");
428577bbf23SMaxime Ripard 		goto err_free_mutex;
429577bbf23SMaxime Ripard 	}
430577bbf23SMaxime Ripard 
431577bbf23SMaxime Ripard 	ret = v4l2_device_register(csi->dev, &csi->v4l);
432577bbf23SMaxime Ripard 	if (ret) {
433577bbf23SMaxime Ripard 		dev_err(csi->dev, "Couldn't register the v4l2 device\n");
434f4ab5d0bSHans Verkuil 		goto err_free_mutex;
435577bbf23SMaxime Ripard 	}
436577bbf23SMaxime Ripard 
437577bbf23SMaxime Ripard 	ret = devm_request_irq(csi->dev, irq, sun4i_csi_irq, 0,
438577bbf23SMaxime Ripard 			       dev_name(csi->dev), csi);
439577bbf23SMaxime Ripard 	if (ret) {
440577bbf23SMaxime Ripard 		dev_err(csi->dev, "Couldn't register our interrupt\n");
441577bbf23SMaxime Ripard 		goto err_unregister_device;
442577bbf23SMaxime Ripard 	}
443577bbf23SMaxime Ripard 
444577bbf23SMaxime Ripard 	return 0;
445577bbf23SMaxime Ripard 
446577bbf23SMaxime Ripard err_unregister_device:
447577bbf23SMaxime Ripard 	v4l2_device_unregister(&csi->v4l);
448577bbf23SMaxime Ripard 
449577bbf23SMaxime Ripard err_free_mutex:
450577bbf23SMaxime Ripard 	mutex_destroy(&csi->lock);
451577bbf23SMaxime Ripard 	return ret;
452577bbf23SMaxime Ripard }
453577bbf23SMaxime Ripard 
sun4i_csi_dma_unregister(struct sun4i_csi * csi)454577bbf23SMaxime Ripard void sun4i_csi_dma_unregister(struct sun4i_csi *csi)
455577bbf23SMaxime Ripard {
456577bbf23SMaxime Ripard 	v4l2_device_unregister(&csi->v4l);
457577bbf23SMaxime Ripard 	mutex_destroy(&csi->lock);
458577bbf23SMaxime Ripard }
459