xref: /linux/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
4  * Author: Yong Deng <yong.deng@magewell.com>
5  * Copyright 2021-2022 Bootlin
6  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
7  */
8 
9 #include <linux/of.h>
10 #include <linux/regmap.h>
11 #include <media/v4l2-device.h>
12 #include <media/v4l2-event.h>
13 #include <media/v4l2-ioctl.h>
14 #include <media/v4l2-mc.h>
15 #include <media/videobuf2-dma-contig.h>
16 #include <media/videobuf2-v4l2.h>
17 
18 #include "sun6i_csi.h"
19 #include "sun6i_csi_bridge.h"
20 #include "sun6i_csi_capture.h"
21 #include "sun6i_csi_reg.h"
22 
23 /* Helpers */
24 
sun6i_csi_capture_dimensions(struct sun6i_csi_device * csi_dev,unsigned int * width,unsigned int * height)25 void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev,
26 				  unsigned int *width, unsigned int *height)
27 {
28 	if (width)
29 		*width = csi_dev->capture.format.fmt.pix.width;
30 	if (height)
31 		*height = csi_dev->capture.format.fmt.pix.height;
32 }
33 
sun6i_csi_capture_format(struct sun6i_csi_device * csi_dev,u32 * pixelformat,u32 * field)34 void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev,
35 			      u32 *pixelformat, u32 *field)
36 {
37 	if (pixelformat)
38 		*pixelformat = csi_dev->capture.format.fmt.pix.pixelformat;
39 
40 	if (field)
41 		*field = csi_dev->capture.format.fmt.pix.field;
42 }
43 
44 /* Format */
45 
46 static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = {
47 	/* Bayer */
48 	{
49 		.pixelformat		= V4L2_PIX_FMT_SBGGR8,
50 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
51 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
52 	},
53 	{
54 		.pixelformat		= V4L2_PIX_FMT_SGBRG8,
55 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
56 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
57 	},
58 	{
59 		.pixelformat		= V4L2_PIX_FMT_SGRBG8,
60 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
61 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
62 	},
63 	{
64 		.pixelformat		= V4L2_PIX_FMT_SRGGB8,
65 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
66 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
67 	},
68 	{
69 		.pixelformat		= V4L2_PIX_FMT_SBGGR10,
70 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
71 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
72 	},
73 	{
74 		.pixelformat		= V4L2_PIX_FMT_SGBRG10,
75 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
76 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
77 	},
78 	{
79 		.pixelformat		= V4L2_PIX_FMT_SGRBG10,
80 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
81 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
82 	},
83 	{
84 		.pixelformat		= V4L2_PIX_FMT_SRGGB10,
85 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
86 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
87 	},
88 	{
89 		.pixelformat		= V4L2_PIX_FMT_SBGGR12,
90 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
91 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
92 	},
93 	{
94 		.pixelformat		= V4L2_PIX_FMT_SGBRG12,
95 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
96 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
97 	},
98 	{
99 		.pixelformat		= V4L2_PIX_FMT_SGRBG12,
100 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
101 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
102 	},
103 	{
104 		.pixelformat		= V4L2_PIX_FMT_SRGGB12,
105 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
106 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
107 	},
108 	/* RGB */
109 	{
110 		.pixelformat		= V4L2_PIX_FMT_RGB565,
111 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,
112 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,
113 	},
114 	{
115 		.pixelformat		= V4L2_PIX_FMT_RGB565X,
116 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,
117 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,
118 	},
119 	/* YUV422 */
120 	{
121 		.pixelformat		= V4L2_PIX_FMT_YUYV,
122 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
123 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
124 		.input_format_raw	= true,
125 		.hsize_len_factor	= 2,
126 	},
127 	{
128 		.pixelformat		= V4L2_PIX_FMT_YVYU,
129 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
130 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
131 		.input_format_raw	= true,
132 		.hsize_len_factor	= 2,
133 	},
134 	{
135 		.pixelformat		= V4L2_PIX_FMT_UYVY,
136 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
137 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
138 		.input_format_raw	= true,
139 		.hsize_len_factor	= 2,
140 	},
141 	{
142 		.pixelformat		= V4L2_PIX_FMT_VYUY,
143 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
144 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
145 		.input_format_raw	= true,
146 		.hsize_len_factor	= 2,
147 	},
148 	{
149 		.pixelformat		= V4L2_PIX_FMT_NV16,
150 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,
151 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,
152 	},
153 	{
154 		.pixelformat		= V4L2_PIX_FMT_NV61,
155 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,
156 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,
157 		.input_yuv_seq_invert	= true,
158 	},
159 	{
160 		.pixelformat		= V4L2_PIX_FMT_YUV422P,
161 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P,
162 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P,
163 	},
164 	/* YUV420 */
165 	{
166 		.pixelformat		= V4L2_PIX_FMT_NV12_16L16,
167 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB,
168 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB,
169 	},
170 	{
171 		.pixelformat		= V4L2_PIX_FMT_NV12,
172 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,
173 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,
174 	},
175 	{
176 		.pixelformat		= V4L2_PIX_FMT_NV21,
177 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,
178 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,
179 		.input_yuv_seq_invert	= true,
180 	},
181 
182 	{
183 		.pixelformat		= V4L2_PIX_FMT_YUV420,
184 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,
185 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,
186 	},
187 	{
188 		.pixelformat		= V4L2_PIX_FMT_YVU420,
189 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,
190 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,
191 		.input_yuv_seq_invert	= true,
192 	},
193 	/* Compressed */
194 	{
195 		.pixelformat		= V4L2_PIX_FMT_JPEG,
196 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
197 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
198 	},
199 };
200 
201 const
sun6i_csi_capture_format_find(u32 pixelformat)202 struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat)
203 {
204 	unsigned int i;
205 
206 	for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++)
207 		if (sun6i_csi_capture_formats[i].pixelformat == pixelformat)
208 			return &sun6i_csi_capture_formats[i];
209 
210 	return NULL;
211 }
212 
213 /* RAW formats need an exact match between pixel and mbus formats. */
214 static const
215 struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = {
216 	/* YUV420 */
217 	{
218 		.pixelformat	= V4L2_PIX_FMT_YUYV,
219 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
220 	},
221 	{
222 		.pixelformat	= V4L2_PIX_FMT_YUYV,
223 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_1X16,
224 	},
225 	{
226 		.pixelformat	= V4L2_PIX_FMT_YVYU,
227 		.mbus_code	= MEDIA_BUS_FMT_YVYU8_2X8,
228 	},
229 	{
230 		.pixelformat	= V4L2_PIX_FMT_YVYU,
231 		.mbus_code	= MEDIA_BUS_FMT_YVYU8_1X16,
232 	},
233 	{
234 		.pixelformat	= V4L2_PIX_FMT_UYVY,
235 		.mbus_code	= MEDIA_BUS_FMT_UYVY8_2X8,
236 	},
237 	{
238 		.pixelformat	= V4L2_PIX_FMT_UYVY,
239 		.mbus_code	= MEDIA_BUS_FMT_UYVY8_1X16,
240 	},
241 	{
242 		.pixelformat	= V4L2_PIX_FMT_VYUY,
243 		.mbus_code	= MEDIA_BUS_FMT_VYUY8_2X8,
244 	},
245 	{
246 		.pixelformat	= V4L2_PIX_FMT_VYUY,
247 		.mbus_code	= MEDIA_BUS_FMT_VYUY8_1X16,
248 	},
249 	/* RGB */
250 	{
251 		.pixelformat	= V4L2_PIX_FMT_RGB565,
252 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
253 	},
254 	{
255 		.pixelformat	= V4L2_PIX_FMT_RGB565X,
256 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_BE,
257 	},
258 	/* Bayer */
259 	{
260 		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
261 		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
262 	},
263 	{
264 		.pixelformat	= V4L2_PIX_FMT_SGBRG8,
265 		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,
266 	},
267 	{
268 		.pixelformat	= V4L2_PIX_FMT_SGRBG8,
269 		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,
270 	},
271 	{
272 		.pixelformat	= V4L2_PIX_FMT_SRGGB8,
273 		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,
274 	},
275 	{
276 		.pixelformat	= V4L2_PIX_FMT_SBGGR10,
277 		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
278 	},
279 	{
280 		.pixelformat	= V4L2_PIX_FMT_SGBRG10,
281 		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
282 	},
283 	{
284 		.pixelformat	= V4L2_PIX_FMT_SGRBG10,
285 		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
286 	},
287 	{
288 		.pixelformat	= V4L2_PIX_FMT_SRGGB10,
289 		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
290 	},
291 	{
292 		.pixelformat	= V4L2_PIX_FMT_SBGGR12,
293 		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
294 	},
295 	{
296 		.pixelformat	= V4L2_PIX_FMT_SGBRG12,
297 		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,
298 	},
299 	{
300 		.pixelformat	= V4L2_PIX_FMT_SGRBG12,
301 		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,
302 	},
303 	{
304 		.pixelformat	= V4L2_PIX_FMT_SRGGB12,
305 		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,
306 	},
307 	/* Compressed */
308 	{
309 		.pixelformat	= V4L2_PIX_FMT_JPEG,
310 		.mbus_code	= MEDIA_BUS_FMT_JPEG_1X8,
311 	},
312 };
313 
sun6i_csi_capture_format_match(u32 pixelformat,u32 mbus_code)314 static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code)
315 {
316 	unsigned int i;
317 
318 	for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) {
319 		const struct sun6i_csi_capture_format_match *match =
320 			&sun6i_csi_capture_format_matches[i];
321 
322 		if (match->pixelformat == pixelformat &&
323 		    match->mbus_code == mbus_code)
324 			return true;
325 	}
326 
327 	return false;
328 }
329 
330 /* Capture */
331 
332 static void
sun6i_csi_capture_buffer_configure(struct sun6i_csi_device * csi_dev,struct sun6i_csi_buffer * csi_buffer)333 sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,
334 				   struct sun6i_csi_buffer *csi_buffer)
335 {
336 	struct regmap *regmap = csi_dev->regmap;
337 	const struct v4l2_format_info *info;
338 	struct vb2_buffer *vb2_buffer;
339 	unsigned int width, height;
340 	dma_addr_t address;
341 	u32 pixelformat;
342 
343 	vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
344 	address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);
345 
346 	regmap_write(regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,
347 		     SUN6I_CSI_ADDR_VALUE(address));
348 
349 	sun6i_csi_capture_dimensions(csi_dev, &width, &height);
350 	sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);
351 
352 	info = v4l2_format_info(pixelformat);
353 	/* Unsupported formats are single-plane, so we can stop here. */
354 	if (!info)
355 		return;
356 
357 	if (info->comp_planes > 1) {
358 		address += info->bpp[0] * width * height;
359 
360 		regmap_write(regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,
361 			     SUN6I_CSI_ADDR_VALUE(address));
362 	}
363 
364 	if (info->comp_planes > 2) {
365 		address += info->bpp[1] * DIV_ROUND_UP(width, info->hdiv) *
366 			   DIV_ROUND_UP(height, info->vdiv);
367 
368 		regmap_write(regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,
369 			     SUN6I_CSI_ADDR_VALUE(address));
370 	}
371 }
372 
sun6i_csi_capture_configure(struct sun6i_csi_device * csi_dev)373 void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)
374 {
375 	struct regmap *regmap = csi_dev->regmap;
376 	const struct sun6i_csi_capture_format *format;
377 	const struct v4l2_format_info *info;
378 	u32 hsize_len, vsize_len;
379 	u32 luma_line, chroma_line = 0;
380 	u32 pixelformat, field;
381 	u32 width, height;
382 
383 	sun6i_csi_capture_dimensions(csi_dev, &width, &height);
384 	sun6i_csi_capture_format(csi_dev, &pixelformat, &field);
385 
386 	format = sun6i_csi_capture_format_find(pixelformat);
387 	if (WARN_ON(!format))
388 		return;
389 
390 	hsize_len = width;
391 	vsize_len = height;
392 
393 	/*
394 	 * When using 8-bit raw input/output (for packed YUV), we need to adapt
395 	 * the width to account for the difference in bpp when it's not 8-bit.
396 	 */
397 	if (format->hsize_len_factor)
398 		hsize_len *= format->hsize_len_factor;
399 
400 	regmap_write(regmap, SUN6I_CSI_CH_HSIZE_REG,
401 		     SUN6I_CSI_CH_HSIZE_LEN(hsize_len) |
402 		     SUN6I_CSI_CH_HSIZE_START(0));
403 
404 	regmap_write(regmap, SUN6I_CSI_CH_VSIZE_REG,
405 		     SUN6I_CSI_CH_VSIZE_LEN(vsize_len) |
406 		     SUN6I_CSI_CH_VSIZE_START(0));
407 
408 	switch (pixelformat) {
409 	case V4L2_PIX_FMT_RGB565X:
410 		luma_line = width * 2;
411 		break;
412 	case V4L2_PIX_FMT_NV12_16L16:
413 		luma_line = width;
414 		chroma_line = width;
415 		break;
416 	case V4L2_PIX_FMT_JPEG:
417 		luma_line = width;
418 		break;
419 	default:
420 		info = v4l2_format_info(pixelformat);
421 		if (WARN_ON(!info))
422 			return;
423 
424 		luma_line = width * info->bpp[0];
425 
426 		if (info->comp_planes > 1)
427 			chroma_line = width * info->bpp[1] / info->hdiv;
428 		break;
429 	}
430 
431 	regmap_write(regmap, SUN6I_CSI_CH_BUF_LEN_REG,
432 		     SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(chroma_line) |
433 		     SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(luma_line));
434 }
435 
436 /* State */
437 
sun6i_csi_capture_state_cleanup(struct sun6i_csi_device * csi_dev,bool error)438 static void sun6i_csi_capture_state_cleanup(struct sun6i_csi_device *csi_dev,
439 					    bool error)
440 {
441 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
442 	struct sun6i_csi_buffer **csi_buffer_states[] = {
443 		&state->pending, &state->current, &state->complete,
444 	};
445 	struct sun6i_csi_buffer *csi_buffer;
446 	struct vb2_buffer *vb2_buffer;
447 	unsigned long flags;
448 	unsigned int i;
449 
450 	spin_lock_irqsave(&state->lock, flags);
451 
452 	for (i = 0; i < ARRAY_SIZE(csi_buffer_states); i++) {
453 		csi_buffer = *csi_buffer_states[i];
454 		if (!csi_buffer)
455 			continue;
456 
457 		vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
458 		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
459 				VB2_BUF_STATE_QUEUED);
460 
461 		*csi_buffer_states[i] = NULL;
462 	}
463 
464 	list_for_each_entry(csi_buffer, &state->queue, list) {
465 		vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
466 		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
467 				VB2_BUF_STATE_QUEUED);
468 	}
469 
470 	INIT_LIST_HEAD(&state->queue);
471 
472 	spin_unlock_irqrestore(&state->lock, flags);
473 }
474 
sun6i_csi_capture_state_update(struct sun6i_csi_device * csi_dev)475 void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev)
476 {
477 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
478 	struct sun6i_csi_buffer *csi_buffer;
479 	unsigned long flags;
480 
481 	spin_lock_irqsave(&state->lock, flags);
482 
483 	if (list_empty(&state->queue))
484 		goto complete;
485 
486 	if (state->pending)
487 		goto complete;
488 
489 	csi_buffer = list_first_entry(&state->queue, struct sun6i_csi_buffer,
490 				      list);
491 
492 	sun6i_csi_capture_buffer_configure(csi_dev, csi_buffer);
493 
494 	list_del(&csi_buffer->list);
495 
496 	state->pending = csi_buffer;
497 
498 complete:
499 	spin_unlock_irqrestore(&state->lock, flags);
500 }
501 
sun6i_csi_capture_state_complete(struct sun6i_csi_device * csi_dev)502 static void sun6i_csi_capture_state_complete(struct sun6i_csi_device *csi_dev)
503 {
504 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
505 	unsigned long flags;
506 
507 	spin_lock_irqsave(&state->lock, flags);
508 
509 	if (!state->pending)
510 		goto complete;
511 
512 	state->complete = state->current;
513 	state->current = state->pending;
514 	state->pending = NULL;
515 
516 	if (state->complete) {
517 		struct sun6i_csi_buffer *csi_buffer = state->complete;
518 		struct vb2_buffer *vb2_buffer =
519 			&csi_buffer->v4l2_buffer.vb2_buf;
520 
521 		vb2_buffer->timestamp = ktime_get_ns();
522 		csi_buffer->v4l2_buffer.sequence = state->sequence;
523 
524 		vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);
525 
526 		state->complete = NULL;
527 	}
528 
529 complete:
530 	spin_unlock_irqrestore(&state->lock, flags);
531 }
532 
sun6i_csi_capture_frame_done(struct sun6i_csi_device * csi_dev)533 void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)
534 {
535 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
536 	unsigned long flags;
537 
538 	spin_lock_irqsave(&state->lock, flags);
539 	state->sequence++;
540 	spin_unlock_irqrestore(&state->lock, flags);
541 }
542 
sun6i_csi_capture_sync(struct sun6i_csi_device * csi_dev)543 void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev)
544 {
545 	sun6i_csi_capture_state_complete(csi_dev);
546 	sun6i_csi_capture_state_update(csi_dev);
547 }
548 
549 /* Queue */
550 
sun6i_csi_capture_queue_setup(struct vb2_queue * queue,unsigned int * buffers_count,unsigned int * planes_count,unsigned int sizes[],struct device * alloc_devs[])551 static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,
552 					 unsigned int *buffers_count,
553 					 unsigned int *planes_count,
554 					 unsigned int sizes[],
555 					 struct device *alloc_devs[])
556 {
557 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
558 	unsigned int size = csi_dev->capture.format.fmt.pix.sizeimage;
559 
560 	if (*planes_count)
561 		return sizes[0] < size ? -EINVAL : 0;
562 
563 	*planes_count = 1;
564 	sizes[0] = size;
565 
566 	return 0;
567 }
568 
sun6i_csi_capture_buffer_prepare(struct vb2_buffer * buffer)569 static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
570 {
571 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
572 	struct sun6i_csi_capture *capture = &csi_dev->capture;
573 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
574 	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
575 	unsigned long size = capture->format.fmt.pix.sizeimage;
576 
577 	if (vb2_plane_size(buffer, 0) < size) {
578 		v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",
579 			 vb2_plane_size(buffer, 0), size);
580 		return -EINVAL;
581 	}
582 
583 	vb2_set_plane_payload(buffer, 0, size);
584 
585 	v4l2_buffer->field = capture->format.fmt.pix.field;
586 
587 	return 0;
588 }
589 
sun6i_csi_capture_buffer_queue(struct vb2_buffer * buffer)590 static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer)
591 {
592 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
593 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
594 	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
595 	struct sun6i_csi_buffer *csi_buffer =
596 		container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
597 	unsigned long flags;
598 
599 	spin_lock_irqsave(&state->lock, flags);
600 	list_add_tail(&csi_buffer->list, &state->queue);
601 	spin_unlock_irqrestore(&state->lock, flags);
602 }
603 
sun6i_csi_capture_start_streaming(struct vb2_queue * queue,unsigned int count)604 static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
605 					     unsigned int count)
606 {
607 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
608 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
609 	struct video_device *video_dev = &csi_dev->capture.video_dev;
610 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
611 	int ret;
612 
613 	state->sequence = 0;
614 
615 	ret = video_device_pipeline_alloc_start(video_dev);
616 	if (ret < 0)
617 		goto error_state;
618 
619 	state->streaming = true;
620 
621 	ret = v4l2_subdev_call(subdev, video, s_stream, 1);
622 	if (ret && ret != -ENOIOCTLCMD)
623 		goto error_streaming;
624 
625 	return 0;
626 
627 error_streaming:
628 	state->streaming = false;
629 
630 	video_device_pipeline_stop(video_dev);
631 
632 error_state:
633 	sun6i_csi_capture_state_cleanup(csi_dev, false);
634 
635 	return ret;
636 }
637 
sun6i_csi_capture_stop_streaming(struct vb2_queue * queue)638 static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)
639 {
640 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
641 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
642 	struct video_device *video_dev = &csi_dev->capture.video_dev;
643 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
644 
645 	v4l2_subdev_call(subdev, video, s_stream, 0);
646 
647 	state->streaming = false;
648 
649 	video_device_pipeline_stop(video_dev);
650 
651 	sun6i_csi_capture_state_cleanup(csi_dev, true);
652 }
653 
654 static const struct vb2_ops sun6i_csi_capture_queue_ops = {
655 	.queue_setup		= sun6i_csi_capture_queue_setup,
656 	.buf_prepare		= sun6i_csi_capture_buffer_prepare,
657 	.buf_queue		= sun6i_csi_capture_buffer_queue,
658 	.start_streaming	= sun6i_csi_capture_start_streaming,
659 	.stop_streaming		= sun6i_csi_capture_stop_streaming,
660 	.wait_prepare		= vb2_ops_wait_prepare,
661 	.wait_finish		= vb2_ops_wait_finish,
662 };
663 
664 /* V4L2 Device */
665 
sun6i_csi_capture_format_prepare(struct v4l2_format * format)666 static void sun6i_csi_capture_format_prepare(struct v4l2_format *format)
667 {
668 	struct v4l2_pix_format *pix_format = &format->fmt.pix;
669 	const struct v4l2_format_info *info;
670 	unsigned int width, height;
671 
672 	v4l_bound_align_image(&pix_format->width, SUN6I_CSI_CAPTURE_WIDTH_MIN,
673 			      SUN6I_CSI_CAPTURE_WIDTH_MAX, 1,
674 			      &pix_format->height, SUN6I_CSI_CAPTURE_HEIGHT_MIN,
675 			      SUN6I_CSI_CAPTURE_HEIGHT_MAX, 1, 0);
676 
677 	if (!sun6i_csi_capture_format_find(pix_format->pixelformat))
678 		pix_format->pixelformat =
679 			sun6i_csi_capture_formats[0].pixelformat;
680 
681 	width = pix_format->width;
682 	height = pix_format->height;
683 
684 	info = v4l2_format_info(pix_format->pixelformat);
685 
686 	switch (pix_format->pixelformat) {
687 	case V4L2_PIX_FMT_NV12_16L16:
688 		pix_format->bytesperline = width * 12 / 8;
689 		pix_format->sizeimage = pix_format->bytesperline * height;
690 		break;
691 	case V4L2_PIX_FMT_JPEG:
692 		pix_format->bytesperline = width;
693 		pix_format->sizeimage = pix_format->bytesperline * height;
694 		break;
695 	default:
696 		v4l2_fill_pixfmt(pix_format, pix_format->pixelformat,
697 				 width, height);
698 		break;
699 	}
700 
701 	if (pix_format->field == V4L2_FIELD_ANY)
702 		pix_format->field = V4L2_FIELD_NONE;
703 
704 	if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG)
705 		pix_format->colorspace = V4L2_COLORSPACE_JPEG;
706 	else if (info && info->pixel_enc == V4L2_PIXEL_ENC_BAYER)
707 		pix_format->colorspace = V4L2_COLORSPACE_RAW;
708 	else
709 		pix_format->colorspace = V4L2_COLORSPACE_SRGB;
710 
711 	pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
712 	pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;
713 	pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
714 }
715 
sun6i_csi_capture_querycap(struct file * file,void * private,struct v4l2_capability * capability)716 static int sun6i_csi_capture_querycap(struct file *file, void *private,
717 				      struct v4l2_capability *capability)
718 {
719 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
720 	struct video_device *video_dev = &csi_dev->capture.video_dev;
721 
722 	strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver));
723 	strscpy(capability->card, video_dev->name, sizeof(capability->card));
724 	snprintf(capability->bus_info, sizeof(capability->bus_info),
725 		 "platform:%s", dev_name(csi_dev->dev));
726 
727 	return 0;
728 }
729 
sun6i_csi_capture_enum_fmt(struct file * file,void * private,struct v4l2_fmtdesc * fmtdesc)730 static int sun6i_csi_capture_enum_fmt(struct file *file, void *private,
731 				      struct v4l2_fmtdesc *fmtdesc)
732 {
733 	u32 index = fmtdesc->index;
734 
735 	if (index >= ARRAY_SIZE(sun6i_csi_capture_formats))
736 		return -EINVAL;
737 
738 	fmtdesc->pixelformat = sun6i_csi_capture_formats[index].pixelformat;
739 
740 	return 0;
741 }
742 
sun6i_csi_capture_g_fmt(struct file * file,void * private,struct v4l2_format * format)743 static int sun6i_csi_capture_g_fmt(struct file *file, void *private,
744 				   struct v4l2_format *format)
745 {
746 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
747 
748 	*format = csi_dev->capture.format;
749 
750 	return 0;
751 }
752 
sun6i_csi_capture_s_fmt(struct file * file,void * private,struct v4l2_format * format)753 static int sun6i_csi_capture_s_fmt(struct file *file, void *private,
754 				   struct v4l2_format *format)
755 {
756 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
757 	struct sun6i_csi_capture *capture = &csi_dev->capture;
758 
759 	if (vb2_is_busy(&capture->queue))
760 		return -EBUSY;
761 
762 	sun6i_csi_capture_format_prepare(format);
763 
764 	csi_dev->capture.format = *format;
765 
766 	return 0;
767 }
768 
sun6i_csi_capture_try_fmt(struct file * file,void * private,struct v4l2_format * format)769 static int sun6i_csi_capture_try_fmt(struct file *file, void *private,
770 				     struct v4l2_format *format)
771 {
772 	sun6i_csi_capture_format_prepare(format);
773 
774 	return 0;
775 }
776 
sun6i_csi_capture_enum_input(struct file * file,void * private,struct v4l2_input * input)777 static int sun6i_csi_capture_enum_input(struct file *file, void *private,
778 					struct v4l2_input *input)
779 {
780 	if (input->index != 0)
781 		return -EINVAL;
782 
783 	input->type = V4L2_INPUT_TYPE_CAMERA;
784 	strscpy(input->name, "Camera", sizeof(input->name));
785 
786 	return 0;
787 }
788 
sun6i_csi_capture_g_input(struct file * file,void * private,unsigned int * index)789 static int sun6i_csi_capture_g_input(struct file *file, void *private,
790 				     unsigned int *index)
791 {
792 	*index = 0;
793 
794 	return 0;
795 }
796 
sun6i_csi_capture_s_input(struct file * file,void * private,unsigned int index)797 static int sun6i_csi_capture_s_input(struct file *file, void *private,
798 				     unsigned int index)
799 {
800 	if (index != 0)
801 		return -EINVAL;
802 
803 	return 0;
804 }
805 
806 static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = {
807 	.vidioc_querycap		= sun6i_csi_capture_querycap,
808 
809 	.vidioc_enum_fmt_vid_cap	= sun6i_csi_capture_enum_fmt,
810 	.vidioc_g_fmt_vid_cap		= sun6i_csi_capture_g_fmt,
811 	.vidioc_s_fmt_vid_cap		= sun6i_csi_capture_s_fmt,
812 	.vidioc_try_fmt_vid_cap		= sun6i_csi_capture_try_fmt,
813 
814 	.vidioc_enum_input		= sun6i_csi_capture_enum_input,
815 	.vidioc_g_input			= sun6i_csi_capture_g_input,
816 	.vidioc_s_input			= sun6i_csi_capture_s_input,
817 
818 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
819 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
820 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
821 	.vidioc_querybuf		= vb2_ioctl_querybuf,
822 	.vidioc_expbuf			= vb2_ioctl_expbuf,
823 	.vidioc_qbuf			= vb2_ioctl_qbuf,
824 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
825 	.vidioc_streamon		= vb2_ioctl_streamon,
826 	.vidioc_streamoff		= vb2_ioctl_streamoff,
827 };
828 
829 /* V4L2 File */
830 
sun6i_csi_capture_open(struct file * file)831 static int sun6i_csi_capture_open(struct file *file)
832 {
833 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
834 	struct sun6i_csi_capture *capture = &csi_dev->capture;
835 	int ret;
836 
837 	if (mutex_lock_interruptible(&capture->lock))
838 		return -ERESTARTSYS;
839 
840 	ret = v4l2_pipeline_pm_get(&capture->video_dev.entity);
841 	if (ret < 0)
842 		goto error_lock;
843 
844 	ret = v4l2_fh_open(file);
845 	if (ret < 0)
846 		goto error_pipeline;
847 
848 	mutex_unlock(&capture->lock);
849 
850 	return 0;
851 
852 error_pipeline:
853 	v4l2_pipeline_pm_put(&capture->video_dev.entity);
854 
855 error_lock:
856 	mutex_unlock(&capture->lock);
857 
858 	return ret;
859 }
860 
sun6i_csi_capture_close(struct file * file)861 static int sun6i_csi_capture_close(struct file *file)
862 {
863 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
864 	struct sun6i_csi_capture *capture = &csi_dev->capture;
865 
866 	mutex_lock(&capture->lock);
867 
868 	_vb2_fop_release(file, NULL);
869 	v4l2_pipeline_pm_put(&capture->video_dev.entity);
870 
871 	mutex_unlock(&capture->lock);
872 
873 	return 0;
874 }
875 
876 static const struct v4l2_file_operations sun6i_csi_capture_fops = {
877 	.owner		= THIS_MODULE,
878 	.open		= sun6i_csi_capture_open,
879 	.release	= sun6i_csi_capture_close,
880 	.unlocked_ioctl	= video_ioctl2,
881 	.mmap		= vb2_fop_mmap,
882 	.poll		= vb2_fop_poll
883 };
884 
885 /* Media Entity */
886 
sun6i_csi_capture_link_validate(struct media_link * link)887 static int sun6i_csi_capture_link_validate(struct media_link *link)
888 {
889 	struct video_device *video_dev =
890 		media_entity_to_video_device(link->sink->entity);
891 	struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);
892 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
893 	const struct sun6i_csi_capture_format *capture_format;
894 	const struct sun6i_csi_bridge_format *bridge_format;
895 	unsigned int capture_width, capture_height;
896 	unsigned int bridge_width, bridge_height;
897 	const struct v4l2_format_info *format_info;
898 	u32 pixelformat, capture_field;
899 	u32 mbus_code, bridge_field;
900 	bool match;
901 
902 	sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);
903 
904 	sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field);
905 	capture_format = sun6i_csi_capture_format_find(pixelformat);
906 	if (WARN_ON(!capture_format))
907 		return -EINVAL;
908 
909 	sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);
910 
911 	sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);
912 	bridge_format = sun6i_csi_bridge_format_find(mbus_code);
913 	if (WARN_ON(!bridge_format))
914 		return -EINVAL;
915 
916 	/* No cropping/scaling is supported. */
917 	if (capture_width != bridge_width || capture_height != bridge_height) {
918 		v4l2_err(v4l2_dev,
919 			 "invalid input/output dimensions: %ux%u/%ux%u\n",
920 			 bridge_width, bridge_height, capture_width,
921 			 capture_height);
922 		return -EINVAL;
923 	}
924 
925 	format_info = v4l2_format_info(pixelformat);
926 	/* Some formats are not listed. */
927 	if (!format_info)
928 		return 0;
929 
930 	if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
931 	    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
932 		goto invalid;
933 
934 	if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB &&
935 	    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
936 		goto invalid;
937 
938 	if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
939 		if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 &&
940 		    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422)
941 			goto invalid;
942 
943 		/* YUV420 input can't produce YUV422 output. */
944 		if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 &&
945 		    format_info->vdiv == 1)
946 			goto invalid;
947 	}
948 
949 	/* With raw input mode, we need a 1:1 match between input and output. */
950 	if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||
951 	    capture_format->input_format_raw) {
952 		match = sun6i_csi_capture_format_match(pixelformat, mbus_code);
953 		if (!match)
954 			goto invalid;
955 	}
956 
957 	return 0;
958 
959 invalid:
960 	v4l2_err(v4l2_dev, "invalid input/output format combination\n");
961 	return -EINVAL;
962 }
963 
964 static const struct media_entity_operations sun6i_csi_capture_media_ops = {
965 	.link_validate = sun6i_csi_capture_link_validate
966 };
967 
968 /* Capture */
969 
sun6i_csi_capture_setup(struct sun6i_csi_device * csi_dev)970 int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
971 {
972 	struct sun6i_csi_capture *capture = &csi_dev->capture;
973 	struct sun6i_csi_capture_state *state = &capture->state;
974 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
975 	struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;
976 	struct video_device *video_dev = &capture->video_dev;
977 	struct vb2_queue *queue = &capture->queue;
978 	struct media_pad *pad = &capture->pad;
979 	struct v4l2_format *format = &csi_dev->capture.format;
980 	struct v4l2_pix_format *pix_format = &format->fmt.pix;
981 	int ret;
982 
983 	/* This may happen with multiple bridge notifier bound calls. */
984 	if (state->setup)
985 		return 0;
986 
987 	/* State */
988 
989 	INIT_LIST_HEAD(&state->queue);
990 	spin_lock_init(&state->lock);
991 
992 	/* Media Entity */
993 
994 	video_dev->entity.ops = &sun6i_csi_capture_media_ops;
995 
996 	/* Media Pad */
997 
998 	pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
999 
1000 	ret = media_entity_pads_init(&video_dev->entity, 1, pad);
1001 	if (ret < 0)
1002 		return ret;
1003 
1004 	/* Queue */
1005 
1006 	mutex_init(&capture->lock);
1007 
1008 	queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1009 	queue->io_modes = VB2_MMAP | VB2_DMABUF;
1010 	queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);
1011 	queue->ops = &sun6i_csi_capture_queue_ops;
1012 	queue->mem_ops = &vb2_dma_contig_memops;
1013 	queue->min_queued_buffers = 2;
1014 	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1015 	queue->lock = &capture->lock;
1016 	queue->dev = csi_dev->dev;
1017 	queue->drv_priv = csi_dev;
1018 
1019 	ret = vb2_queue_init(queue);
1020 	if (ret) {
1021 		v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
1022 		goto error_media_entity;
1023 	}
1024 
1025 	/* V4L2 Format */
1026 
1027 	format->type = queue->type;
1028 	pix_format->pixelformat = sun6i_csi_capture_formats[0].pixelformat;
1029 	pix_format->width = 1280;
1030 	pix_format->height = 720;
1031 	pix_format->field = V4L2_FIELD_NONE;
1032 
1033 	sun6i_csi_capture_format_prepare(format);
1034 
1035 	/* Video Device */
1036 
1037 	strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME,
1038 		sizeof(video_dev->name));
1039 	video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
1040 	video_dev->vfl_dir = VFL_DIR_RX;
1041 	video_dev->release = video_device_release_empty;
1042 	video_dev->fops = &sun6i_csi_capture_fops;
1043 	video_dev->ioctl_ops = &sun6i_csi_capture_ioctl_ops;
1044 	video_dev->v4l2_dev = v4l2_dev;
1045 	video_dev->queue = queue;
1046 	video_dev->lock = &capture->lock;
1047 
1048 	video_set_drvdata(video_dev, csi_dev);
1049 
1050 	ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
1051 	if (ret < 0) {
1052 		v4l2_err(v4l2_dev, "failed to register video device: %d\n",
1053 			 ret);
1054 		goto error_media_entity;
1055 	}
1056 
1057 	/* Media Pad Link */
1058 
1059 	ret = media_create_pad_link(&bridge_subdev->entity,
1060 				    SUN6I_CSI_BRIDGE_PAD_SOURCE,
1061 				    &video_dev->entity, 0,
1062 				    csi_dev->isp_available ? 0 :
1063 				    MEDIA_LNK_FL_ENABLED |
1064 				    MEDIA_LNK_FL_IMMUTABLE);
1065 	if (ret < 0) {
1066 		v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",
1067 			 bridge_subdev->entity.name,
1068 			 SUN6I_CSI_BRIDGE_PAD_SOURCE,
1069 			 video_dev->entity.name, 0);
1070 		goto error_video_device;
1071 	}
1072 
1073 	state->setup = true;
1074 
1075 	return 0;
1076 
1077 error_video_device:
1078 	vb2_video_unregister_device(video_dev);
1079 
1080 error_media_entity:
1081 	media_entity_cleanup(&video_dev->entity);
1082 
1083 	mutex_destroy(&capture->lock);
1084 
1085 	return ret;
1086 }
1087 
sun6i_csi_capture_cleanup(struct sun6i_csi_device * csi_dev)1088 void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev)
1089 {
1090 	struct sun6i_csi_capture *capture = &csi_dev->capture;
1091 	struct video_device *video_dev = &capture->video_dev;
1092 
1093 	/* This may happen if async registration failed to complete. */
1094 	if (!capture->state.setup)
1095 		return;
1096 
1097 	vb2_video_unregister_device(video_dev);
1098 	media_entity_cleanup(&video_dev->entity);
1099 	mutex_destroy(&capture->lock);
1100 
1101 	capture->state.setup = false;
1102 }
1103