1e9201cb2SPaul Kocialkowski // SPDX-License-Identifier: GPL-2.0+
2e9201cb2SPaul Kocialkowski /*
3e9201cb2SPaul Kocialkowski  * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
4e9201cb2SPaul Kocialkowski  * Author: Yong Deng <yong.deng@magewell.com>
5dc85e4cdSPaul Kocialkowski  * Copyright 2021-2022 Bootlin
6dc85e4cdSPaul Kocialkowski  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
7e9201cb2SPaul Kocialkowski  */
8e9201cb2SPaul Kocialkowski 
9e9201cb2SPaul Kocialkowski #include <linux/of.h>
10dc8b931cSPaul Kocialkowski #include <linux/regmap.h>
11e9201cb2SPaul Kocialkowski #include <media/v4l2-device.h>
12e9201cb2SPaul Kocialkowski #include <media/v4l2-event.h>
13e9201cb2SPaul Kocialkowski #include <media/v4l2-ioctl.h>
14e9201cb2SPaul Kocialkowski #include <media/v4l2-mc.h>
15e9201cb2SPaul Kocialkowski #include <media/videobuf2-dma-contig.h>
16e9201cb2SPaul Kocialkowski #include <media/videobuf2-v4l2.h>
17e9201cb2SPaul Kocialkowski 
18e9201cb2SPaul Kocialkowski #include "sun6i_csi.h"
1983b1356bSPaul Kocialkowski #include "sun6i_csi_bridge.h"
20e9201cb2SPaul Kocialkowski #include "sun6i_csi_capture.h"
21dc8b931cSPaul Kocialkowski #include "sun6i_csi_reg.h"
22e9201cb2SPaul Kocialkowski 
23e9201cb2SPaul Kocialkowski /* Helpers */
24e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_dimensions(struct sun6i_csi_device * csi_dev,unsigned int * width,unsigned int * height)250f6417f1SPaul Kocialkowski void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev,
260f6417f1SPaul Kocialkowski 				  unsigned int *width, unsigned int *height)
270f6417f1SPaul Kocialkowski {
280f6417f1SPaul Kocialkowski 	if (width)
290f6417f1SPaul Kocialkowski 		*width = csi_dev->capture.format.fmt.pix.width;
300f6417f1SPaul Kocialkowski 	if (height)
310f6417f1SPaul Kocialkowski 		*height = csi_dev->capture.format.fmt.pix.height;
320f6417f1SPaul Kocialkowski }
330f6417f1SPaul Kocialkowski 
sun6i_csi_capture_format(struct sun6i_csi_device * csi_dev,u32 * pixelformat,u32 * field)340f6417f1SPaul Kocialkowski void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev,
350f6417f1SPaul Kocialkowski 			      u32 *pixelformat, u32 *field)
360f6417f1SPaul Kocialkowski {
370f6417f1SPaul Kocialkowski 	if (pixelformat)
380f6417f1SPaul Kocialkowski 		*pixelformat = csi_dev->capture.format.fmt.pix.pixelformat;
390f6417f1SPaul Kocialkowski 
400f6417f1SPaul Kocialkowski 	if (field)
410f6417f1SPaul Kocialkowski 		*field = csi_dev->capture.format.fmt.pix.field;
420f6417f1SPaul Kocialkowski }
430f6417f1SPaul Kocialkowski 
44e9201cb2SPaul Kocialkowski /* Format */
45e9201cb2SPaul Kocialkowski 
4653fd3926SPaul Kocialkowski static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = {
4753fd3926SPaul Kocialkowski 	/* Bayer */
4853fd3926SPaul Kocialkowski 	{
4953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SBGGR8,
5053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
5153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
5253fd3926SPaul Kocialkowski 	},
5353fd3926SPaul Kocialkowski 	{
5453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SGBRG8,
5553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
5653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
5753fd3926SPaul Kocialkowski 	},
5853fd3926SPaul Kocialkowski 	{
5953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SGRBG8,
6053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
6153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
6253fd3926SPaul Kocialkowski 	},
6353fd3926SPaul Kocialkowski 	{
6453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SRGGB8,
6553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
6653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
6753fd3926SPaul Kocialkowski 	},
6853fd3926SPaul Kocialkowski 	{
6953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SBGGR10,
7053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
7153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
7253fd3926SPaul Kocialkowski 	},
7353fd3926SPaul Kocialkowski 	{
7453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SGBRG10,
7553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
7653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
7753fd3926SPaul Kocialkowski 	},
7853fd3926SPaul Kocialkowski 	{
7953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SGRBG10,
8053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
8153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
8253fd3926SPaul Kocialkowski 	},
8353fd3926SPaul Kocialkowski 	{
8453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SRGGB10,
8553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,
8653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,
8753fd3926SPaul Kocialkowski 	},
8853fd3926SPaul Kocialkowski 	{
8953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SBGGR12,
9053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
9153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
9253fd3926SPaul Kocialkowski 	},
9353fd3926SPaul Kocialkowski 	{
9453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SGBRG12,
9553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
9653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
9753fd3926SPaul Kocialkowski 	},
9853fd3926SPaul Kocialkowski 	{
9953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SGRBG12,
10053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
10153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
10253fd3926SPaul Kocialkowski 	},
10353fd3926SPaul Kocialkowski 	{
10453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_SRGGB12,
10553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,
10653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,
10753fd3926SPaul Kocialkowski 	},
10853fd3926SPaul Kocialkowski 	/* RGB */
10953fd3926SPaul Kocialkowski 	{
11053fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_RGB565,
11153fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,
11253fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,
11353fd3926SPaul Kocialkowski 	},
11453fd3926SPaul Kocialkowski 	{
11553fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_RGB565X,
11653fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,
11753fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,
11853fd3926SPaul Kocialkowski 	},
11953fd3926SPaul Kocialkowski 	/* YUV422 */
12053fd3926SPaul Kocialkowski 	{
12153fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_YUYV,
12253fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
12353fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
12453fd3926SPaul Kocialkowski 		.input_format_raw	= true,
12553fd3926SPaul Kocialkowski 		.hsize_len_factor	= 2,
12653fd3926SPaul Kocialkowski 	},
12753fd3926SPaul Kocialkowski 	{
12853fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_YVYU,
12953fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
13053fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
13153fd3926SPaul Kocialkowski 		.input_format_raw	= true,
13253fd3926SPaul Kocialkowski 		.hsize_len_factor	= 2,
13353fd3926SPaul Kocialkowski 	},
13453fd3926SPaul Kocialkowski 	{
13553fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_UYVY,
13653fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
13753fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
13853fd3926SPaul Kocialkowski 		.input_format_raw	= true,
13953fd3926SPaul Kocialkowski 		.hsize_len_factor	= 2,
14053fd3926SPaul Kocialkowski 	},
14153fd3926SPaul Kocialkowski 	{
14253fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_VYUY,
14353fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
14453fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
14553fd3926SPaul Kocialkowski 		.input_format_raw	= true,
14653fd3926SPaul Kocialkowski 		.hsize_len_factor	= 2,
14753fd3926SPaul Kocialkowski 	},
14853fd3926SPaul Kocialkowski 	{
14953fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_NV16,
15053fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,
15153fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,
15253fd3926SPaul Kocialkowski 	},
15353fd3926SPaul Kocialkowski 	{
15453fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_NV61,
15553fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,
15653fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,
15753fd3926SPaul Kocialkowski 		.input_yuv_seq_invert	= true,
15853fd3926SPaul Kocialkowski 	},
15953fd3926SPaul Kocialkowski 	{
16053fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_YUV422P,
16153fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P,
16253fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P,
16353fd3926SPaul Kocialkowski 	},
16453fd3926SPaul Kocialkowski 	/* YUV420 */
16553fd3926SPaul Kocialkowski 	{
16653fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_NV12_16L16,
16753fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB,
16853fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB,
16953fd3926SPaul Kocialkowski 	},
17053fd3926SPaul Kocialkowski 	{
17153fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_NV12,
17253fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,
17353fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,
17453fd3926SPaul Kocialkowski 	},
17553fd3926SPaul Kocialkowski 	{
17653fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_NV21,
17753fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,
17853fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,
17953fd3926SPaul Kocialkowski 		.input_yuv_seq_invert	= true,
18053fd3926SPaul Kocialkowski 	},
18153fd3926SPaul Kocialkowski 
18253fd3926SPaul Kocialkowski 	{
18353fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_YUV420,
18453fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,
18553fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,
18653fd3926SPaul Kocialkowski 	},
18753fd3926SPaul Kocialkowski 	{
18853fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_YVU420,
18953fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,
19053fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,
19153fd3926SPaul Kocialkowski 		.input_yuv_seq_invert	= true,
19253fd3926SPaul Kocialkowski 	},
19353fd3926SPaul Kocialkowski 	/* Compressed */
19453fd3926SPaul Kocialkowski 	{
19553fd3926SPaul Kocialkowski 		.pixelformat		= V4L2_PIX_FMT_JPEG,
19653fd3926SPaul Kocialkowski 		.output_format_frame	= SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,
19753fd3926SPaul Kocialkowski 		.output_format_field	= SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,
19853fd3926SPaul Kocialkowski 	},
199e9201cb2SPaul Kocialkowski };
200e9201cb2SPaul Kocialkowski 
20153fd3926SPaul Kocialkowski const
sun6i_csi_capture_format_find(u32 pixelformat)20253fd3926SPaul Kocialkowski struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat)
203e9201cb2SPaul Kocialkowski {
204e9201cb2SPaul Kocialkowski 	unsigned int i;
205e9201cb2SPaul Kocialkowski 
206e9201cb2SPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++)
20753fd3926SPaul Kocialkowski 		if (sun6i_csi_capture_formats[i].pixelformat == pixelformat)
20853fd3926SPaul Kocialkowski 			return &sun6i_csi_capture_formats[i];
209e9201cb2SPaul Kocialkowski 
21053fd3926SPaul Kocialkowski 	return NULL;
211e9201cb2SPaul Kocialkowski }
212e9201cb2SPaul Kocialkowski 
2131fd07a80SPaul Kocialkowski /* RAW formats need an exact match between pixel and mbus formats. */
2141fd07a80SPaul Kocialkowski static const
2151fd07a80SPaul Kocialkowski struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = {
2161fd07a80SPaul Kocialkowski 	/* YUV420 */
2171fd07a80SPaul Kocialkowski 	{
2181fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_YUYV,
2191fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
2201fd07a80SPaul Kocialkowski 	},
2211fd07a80SPaul Kocialkowski 	{
2221fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_YUYV,
2231fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_1X16,
2241fd07a80SPaul Kocialkowski 	},
2251fd07a80SPaul Kocialkowski 	{
2261fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_YVYU,
2271fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_YVYU8_2X8,
2281fd07a80SPaul Kocialkowski 	},
2291fd07a80SPaul Kocialkowski 	{
2301fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_YVYU,
2311fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_YVYU8_1X16,
2321fd07a80SPaul Kocialkowski 	},
2331fd07a80SPaul Kocialkowski 	{
2341fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_UYVY,
2351fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_UYVY8_2X8,
2361fd07a80SPaul Kocialkowski 	},
2371fd07a80SPaul Kocialkowski 	{
2381fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_UYVY,
2391fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_UYVY8_1X16,
2401fd07a80SPaul Kocialkowski 	},
2411fd07a80SPaul Kocialkowski 	{
2421fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_VYUY,
2431fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_VYUY8_2X8,
2441fd07a80SPaul Kocialkowski 	},
2451fd07a80SPaul Kocialkowski 	{
2461fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_VYUY,
2471fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_VYUY8_1X16,
2481fd07a80SPaul Kocialkowski 	},
2491fd07a80SPaul Kocialkowski 	/* RGB */
2501fd07a80SPaul Kocialkowski 	{
2511fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_RGB565,
2521fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
2531fd07a80SPaul Kocialkowski 	},
2541fd07a80SPaul Kocialkowski 	{
2551fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_RGB565X,
2561fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_BE,
2571fd07a80SPaul Kocialkowski 	},
2581fd07a80SPaul Kocialkowski 	/* Bayer */
2591fd07a80SPaul Kocialkowski 	{
2601fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
2611fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
2621fd07a80SPaul Kocialkowski 	},
2631fd07a80SPaul Kocialkowski 	{
2641fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SGBRG8,
2651fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,
2661fd07a80SPaul Kocialkowski 	},
2671fd07a80SPaul Kocialkowski 	{
2681fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SGRBG8,
2691fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,
2701fd07a80SPaul Kocialkowski 	},
2711fd07a80SPaul Kocialkowski 	{
2721fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SRGGB8,
2731fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,
2741fd07a80SPaul Kocialkowski 	},
2751fd07a80SPaul Kocialkowski 	{
2761fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SBGGR10,
2771fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
2781fd07a80SPaul Kocialkowski 	},
2791fd07a80SPaul Kocialkowski 	{
2801fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SGBRG10,
2811fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
2821fd07a80SPaul Kocialkowski 	},
2831fd07a80SPaul Kocialkowski 	{
2841fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SGRBG10,
2851fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
2861fd07a80SPaul Kocialkowski 	},
2871fd07a80SPaul Kocialkowski 	{
2881fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SRGGB10,
2891fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
2901fd07a80SPaul Kocialkowski 	},
2911fd07a80SPaul Kocialkowski 	{
2921fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SBGGR12,
2931fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
2941fd07a80SPaul Kocialkowski 	},
2951fd07a80SPaul Kocialkowski 	{
2961fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SGBRG12,
2971fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,
2981fd07a80SPaul Kocialkowski 	},
2991fd07a80SPaul Kocialkowski 	{
3001fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SGRBG12,
3011fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,
3021fd07a80SPaul Kocialkowski 	},
3031fd07a80SPaul Kocialkowski 	{
3041fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_SRGGB12,
3051fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,
3061fd07a80SPaul Kocialkowski 	},
3071fd07a80SPaul Kocialkowski 	/* Compressed */
3081fd07a80SPaul Kocialkowski 	{
3091fd07a80SPaul Kocialkowski 		.pixelformat	= V4L2_PIX_FMT_JPEG,
3101fd07a80SPaul Kocialkowski 		.mbus_code	= MEDIA_BUS_FMT_JPEG_1X8,
3111fd07a80SPaul Kocialkowski 	},
3121fd07a80SPaul Kocialkowski };
3131fd07a80SPaul Kocialkowski 
sun6i_csi_capture_format_match(u32 pixelformat,u32 mbus_code)3141fd07a80SPaul Kocialkowski static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code)
3151fd07a80SPaul Kocialkowski {
3161fd07a80SPaul Kocialkowski 	unsigned int i;
3171fd07a80SPaul Kocialkowski 
3181fd07a80SPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) {
3191fd07a80SPaul Kocialkowski 		const struct sun6i_csi_capture_format_match *match =
3201fd07a80SPaul Kocialkowski 			&sun6i_csi_capture_format_matches[i];
3211fd07a80SPaul Kocialkowski 
3221fd07a80SPaul Kocialkowski 		if (match->pixelformat == pixelformat &&
3231fd07a80SPaul Kocialkowski 		    match->mbus_code == mbus_code)
3241fd07a80SPaul Kocialkowski 			return true;
3251fd07a80SPaul Kocialkowski 	}
3261fd07a80SPaul Kocialkowski 
3271fd07a80SPaul Kocialkowski 	return false;
3281fd07a80SPaul Kocialkowski }
3291fd07a80SPaul Kocialkowski 
330e9201cb2SPaul Kocialkowski /* Capture */
331e9201cb2SPaul Kocialkowski 
332e9201cb2SPaul Kocialkowski static void
sun6i_csi_capture_buffer_configure(struct sun6i_csi_device * csi_dev,struct sun6i_csi_buffer * csi_buffer)333e9201cb2SPaul Kocialkowski sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,
334e9201cb2SPaul Kocialkowski 				   struct sun6i_csi_buffer *csi_buffer)
335e9201cb2SPaul Kocialkowski {
336dc8b931cSPaul Kocialkowski 	struct regmap *regmap = csi_dev->regmap;
337dc8b931cSPaul Kocialkowski 	const struct v4l2_format_info *info;
338b86f6ea0SPaul Kocialkowski 	struct vb2_buffer *vb2_buffer;
339dc8b931cSPaul Kocialkowski 	unsigned int width, height;
340b86f6ea0SPaul Kocialkowski 	dma_addr_t address;
341dc8b931cSPaul Kocialkowski 	u32 pixelformat;
342b86f6ea0SPaul Kocialkowski 
343b86f6ea0SPaul Kocialkowski 	vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
344b86f6ea0SPaul Kocialkowski 	address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);
345b86f6ea0SPaul Kocialkowski 
346dc8b931cSPaul Kocialkowski 	regmap_write(regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,
347dc8b931cSPaul Kocialkowski 		     SUN6I_CSI_ADDR_VALUE(address));
348dc8b931cSPaul Kocialkowski 
349dc8b931cSPaul Kocialkowski 	sun6i_csi_capture_dimensions(csi_dev, &width, &height);
350dc8b931cSPaul Kocialkowski 	sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);
351dc8b931cSPaul Kocialkowski 
352dc8b931cSPaul Kocialkowski 	info = v4l2_format_info(pixelformat);
353dc8b931cSPaul Kocialkowski 	/* Unsupported formats are single-plane, so we can stop here. */
354dc8b931cSPaul Kocialkowski 	if (!info)
355dc8b931cSPaul Kocialkowski 		return;
356dc8b931cSPaul Kocialkowski 
357dc8b931cSPaul Kocialkowski 	if (info->comp_planes > 1) {
358dc8b931cSPaul Kocialkowski 		address += info->bpp[0] * width * height;
359dc8b931cSPaul Kocialkowski 
360dc8b931cSPaul Kocialkowski 		regmap_write(regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,
361dc8b931cSPaul Kocialkowski 			     SUN6I_CSI_ADDR_VALUE(address));
362dc8b931cSPaul Kocialkowski 	}
363dc8b931cSPaul Kocialkowski 
364dc8b931cSPaul Kocialkowski 	if (info->comp_planes > 2) {
365dc8b931cSPaul Kocialkowski 		address += info->bpp[1] * DIV_ROUND_UP(width, info->hdiv) *
366dc8b931cSPaul Kocialkowski 			   DIV_ROUND_UP(height, info->vdiv);
367dc8b931cSPaul Kocialkowski 
368dc8b931cSPaul Kocialkowski 		regmap_write(regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,
369dc8b931cSPaul Kocialkowski 			     SUN6I_CSI_ADDR_VALUE(address));
370dc8b931cSPaul Kocialkowski 	}
371e9201cb2SPaul Kocialkowski }
372e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_configure(struct sun6i_csi_device * csi_dev)373c55d9813SPaul Kocialkowski void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)
374b79dca9bSPaul Kocialkowski {
375eee68463SPaul Kocialkowski 	struct regmap *regmap = csi_dev->regmap;
3769ff5d37cSPaul Kocialkowski 	const struct sun6i_csi_capture_format *format;
377eee68463SPaul Kocialkowski 	const struct v4l2_format_info *info;
378eee68463SPaul Kocialkowski 	u32 hsize_len, vsize_len;
379eee68463SPaul Kocialkowski 	u32 luma_line, chroma_line = 0;
380b79dca9bSPaul Kocialkowski 	u32 pixelformat, field;
381b79dca9bSPaul Kocialkowski 	u32 width, height;
382b79dca9bSPaul Kocialkowski 
383b79dca9bSPaul Kocialkowski 	sun6i_csi_capture_dimensions(csi_dev, &width, &height);
384b79dca9bSPaul Kocialkowski 	sun6i_csi_capture_format(csi_dev, &pixelformat, &field);
385b79dca9bSPaul Kocialkowski 
3869ff5d37cSPaul Kocialkowski 	format = sun6i_csi_capture_format_find(pixelformat);
3879ff5d37cSPaul Kocialkowski 	if (WARN_ON(!format))
3889ff5d37cSPaul Kocialkowski 		return;
3899ff5d37cSPaul Kocialkowski 
390eee68463SPaul Kocialkowski 	hsize_len = width;
391eee68463SPaul Kocialkowski 	vsize_len = height;
392b79dca9bSPaul Kocialkowski 
393eee68463SPaul Kocialkowski 	/*
3949ff5d37cSPaul Kocialkowski 	 * When using 8-bit raw input/output (for packed YUV), we need to adapt
3959ff5d37cSPaul Kocialkowski 	 * the width to account for the difference in bpp when it's not 8-bit.
396eee68463SPaul Kocialkowski 	 */
3979ff5d37cSPaul Kocialkowski 	if (format->hsize_len_factor)
3989ff5d37cSPaul Kocialkowski 		hsize_len *= format->hsize_len_factor;
399b79dca9bSPaul Kocialkowski 
400eee68463SPaul Kocialkowski 	regmap_write(regmap, SUN6I_CSI_CH_HSIZE_REG,
401eee68463SPaul Kocialkowski 		     SUN6I_CSI_CH_HSIZE_LEN(hsize_len) |
402b79dca9bSPaul Kocialkowski 		     SUN6I_CSI_CH_HSIZE_START(0));
403eee68463SPaul Kocialkowski 
404eee68463SPaul Kocialkowski 	regmap_write(regmap, SUN6I_CSI_CH_VSIZE_REG,
405eee68463SPaul Kocialkowski 		     SUN6I_CSI_CH_VSIZE_LEN(vsize_len) |
406b79dca9bSPaul Kocialkowski 		     SUN6I_CSI_CH_VSIZE_START(0));
407b79dca9bSPaul Kocialkowski 
408b79dca9bSPaul Kocialkowski 	switch (pixelformat) {
409eee68463SPaul Kocialkowski 	case V4L2_PIX_FMT_RGB565X:
410eee68463SPaul Kocialkowski 		luma_line = width * 2;
411eee68463SPaul Kocialkowski 		break;
412b79dca9bSPaul Kocialkowski 	case V4L2_PIX_FMT_NV12_16L16:
413eee68463SPaul Kocialkowski 		luma_line = width;
414eee68463SPaul Kocialkowski 		chroma_line = width;
415b79dca9bSPaul Kocialkowski 		break;
416eee68463SPaul Kocialkowski 	case V4L2_PIX_FMT_JPEG:
417eee68463SPaul Kocialkowski 		luma_line = width;
418b79dca9bSPaul Kocialkowski 		break;
419eee68463SPaul Kocialkowski 	default:
420eee68463SPaul Kocialkowski 		info = v4l2_format_info(pixelformat);
421eee68463SPaul Kocialkowski 		if (WARN_ON(!info))
422eee68463SPaul Kocialkowski 			return;
423eee68463SPaul Kocialkowski 
424eee68463SPaul Kocialkowski 		luma_line = width * info->bpp[0];
425eee68463SPaul Kocialkowski 
426eee68463SPaul Kocialkowski 		if (info->comp_planes > 1)
427eee68463SPaul Kocialkowski 			chroma_line = width * info->bpp[1] / info->hdiv;
428b79dca9bSPaul Kocialkowski 		break;
429b79dca9bSPaul Kocialkowski 	}
430b79dca9bSPaul Kocialkowski 
431eee68463SPaul Kocialkowski 	regmap_write(regmap, SUN6I_CSI_CH_BUF_LEN_REG,
432eee68463SPaul Kocialkowski 		     SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(chroma_line) |
433eee68463SPaul Kocialkowski 		     SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(luma_line));
434b79dca9bSPaul Kocialkowski }
435b79dca9bSPaul Kocialkowski 
436b86f6ea0SPaul Kocialkowski /* State */
437b86f6ea0SPaul Kocialkowski 
sun6i_csi_capture_state_cleanup(struct sun6i_csi_device * csi_dev,bool error)438b86f6ea0SPaul Kocialkowski static void sun6i_csi_capture_state_cleanup(struct sun6i_csi_device *csi_dev,
439b86f6ea0SPaul Kocialkowski 					    bool error)
440b86f6ea0SPaul Kocialkowski {
441b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
442b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_buffer **csi_buffer_states[] = {
443b86f6ea0SPaul Kocialkowski 		&state->pending, &state->current, &state->complete,
444b86f6ea0SPaul Kocialkowski 	};
445b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_buffer *csi_buffer;
446b86f6ea0SPaul Kocialkowski 	struct vb2_buffer *vb2_buffer;
447b86f6ea0SPaul Kocialkowski 	unsigned long flags;
448b86f6ea0SPaul Kocialkowski 	unsigned int i;
449b86f6ea0SPaul Kocialkowski 
450b86f6ea0SPaul Kocialkowski 	spin_lock_irqsave(&state->lock, flags);
451b86f6ea0SPaul Kocialkowski 
452b86f6ea0SPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(csi_buffer_states); i++) {
453b86f6ea0SPaul Kocialkowski 		csi_buffer = *csi_buffer_states[i];
454b86f6ea0SPaul Kocialkowski 		if (!csi_buffer)
455b86f6ea0SPaul Kocialkowski 			continue;
456b86f6ea0SPaul Kocialkowski 
457b86f6ea0SPaul Kocialkowski 		vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
458b86f6ea0SPaul Kocialkowski 		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
459b86f6ea0SPaul Kocialkowski 				VB2_BUF_STATE_QUEUED);
460b86f6ea0SPaul Kocialkowski 
461b86f6ea0SPaul Kocialkowski 		*csi_buffer_states[i] = NULL;
462b86f6ea0SPaul Kocialkowski 	}
463b86f6ea0SPaul Kocialkowski 
464b86f6ea0SPaul Kocialkowski 	list_for_each_entry(csi_buffer, &state->queue, list) {
465b86f6ea0SPaul Kocialkowski 		vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
466b86f6ea0SPaul Kocialkowski 		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
467b86f6ea0SPaul Kocialkowski 				VB2_BUF_STATE_QUEUED);
468b86f6ea0SPaul Kocialkowski 	}
469b86f6ea0SPaul Kocialkowski 
470b86f6ea0SPaul Kocialkowski 	INIT_LIST_HEAD(&state->queue);
471b86f6ea0SPaul Kocialkowski 
472b86f6ea0SPaul Kocialkowski 	spin_unlock_irqrestore(&state->lock, flags);
473b86f6ea0SPaul Kocialkowski }
474b86f6ea0SPaul Kocialkowski 
sun6i_csi_capture_state_update(struct sun6i_csi_device * csi_dev)475c55d9813SPaul Kocialkowski void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev)
476b86f6ea0SPaul Kocialkowski {
477b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
478b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_buffer *csi_buffer;
479b86f6ea0SPaul Kocialkowski 	unsigned long flags;
480b86f6ea0SPaul Kocialkowski 
481b86f6ea0SPaul Kocialkowski 	spin_lock_irqsave(&state->lock, flags);
482b86f6ea0SPaul Kocialkowski 
483b86f6ea0SPaul Kocialkowski 	if (list_empty(&state->queue))
484b86f6ea0SPaul Kocialkowski 		goto complete;
485b86f6ea0SPaul Kocialkowski 
486b86f6ea0SPaul Kocialkowski 	if (state->pending)
487b86f6ea0SPaul Kocialkowski 		goto complete;
488b86f6ea0SPaul Kocialkowski 
489b86f6ea0SPaul Kocialkowski 	csi_buffer = list_first_entry(&state->queue, struct sun6i_csi_buffer,
490b86f6ea0SPaul Kocialkowski 				      list);
491b86f6ea0SPaul Kocialkowski 
492b86f6ea0SPaul Kocialkowski 	sun6i_csi_capture_buffer_configure(csi_dev, csi_buffer);
493b86f6ea0SPaul Kocialkowski 
494b86f6ea0SPaul Kocialkowski 	list_del(&csi_buffer->list);
495b86f6ea0SPaul Kocialkowski 
496b86f6ea0SPaul Kocialkowski 	state->pending = csi_buffer;
497b86f6ea0SPaul Kocialkowski 
498b86f6ea0SPaul Kocialkowski complete:
499b86f6ea0SPaul Kocialkowski 	spin_unlock_irqrestore(&state->lock, flags);
500b86f6ea0SPaul Kocialkowski }
501b86f6ea0SPaul Kocialkowski 
sun6i_csi_capture_state_complete(struct sun6i_csi_device * csi_dev)502b86f6ea0SPaul Kocialkowski static void sun6i_csi_capture_state_complete(struct sun6i_csi_device *csi_dev)
503b86f6ea0SPaul Kocialkowski {
504b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
505b86f6ea0SPaul Kocialkowski 	unsigned long flags;
506b86f6ea0SPaul Kocialkowski 
507b86f6ea0SPaul Kocialkowski 	spin_lock_irqsave(&state->lock, flags);
508b86f6ea0SPaul Kocialkowski 
509b86f6ea0SPaul Kocialkowski 	if (!state->pending)
510b86f6ea0SPaul Kocialkowski 		goto complete;
511b86f6ea0SPaul Kocialkowski 
512b86f6ea0SPaul Kocialkowski 	state->complete = state->current;
513b86f6ea0SPaul Kocialkowski 	state->current = state->pending;
514b86f6ea0SPaul Kocialkowski 	state->pending = NULL;
515b86f6ea0SPaul Kocialkowski 
516b86f6ea0SPaul Kocialkowski 	if (state->complete) {
517b86f6ea0SPaul Kocialkowski 		struct sun6i_csi_buffer *csi_buffer = state->complete;
518b86f6ea0SPaul Kocialkowski 		struct vb2_buffer *vb2_buffer =
519b86f6ea0SPaul Kocialkowski 			&csi_buffer->v4l2_buffer.vb2_buf;
520b86f6ea0SPaul Kocialkowski 
521b86f6ea0SPaul Kocialkowski 		vb2_buffer->timestamp = ktime_get_ns();
522b86f6ea0SPaul Kocialkowski 		csi_buffer->v4l2_buffer.sequence = state->sequence;
523b86f6ea0SPaul Kocialkowski 
524b86f6ea0SPaul Kocialkowski 		vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);
525b86f6ea0SPaul Kocialkowski 
526b86f6ea0SPaul Kocialkowski 		state->complete = NULL;
527b86f6ea0SPaul Kocialkowski 	}
528b86f6ea0SPaul Kocialkowski 
529b86f6ea0SPaul Kocialkowski complete:
530b86f6ea0SPaul Kocialkowski 	spin_unlock_irqrestore(&state->lock, flags);
531b86f6ea0SPaul Kocialkowski }
532b86f6ea0SPaul Kocialkowski 
sun6i_csi_capture_frame_done(struct sun6i_csi_device * csi_dev)533b86f6ea0SPaul Kocialkowski void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)
534b86f6ea0SPaul Kocialkowski {
535b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
536b86f6ea0SPaul Kocialkowski 	unsigned long flags;
537b86f6ea0SPaul Kocialkowski 
538b86f6ea0SPaul Kocialkowski 	spin_lock_irqsave(&state->lock, flags);
539b86f6ea0SPaul Kocialkowski 	state->sequence++;
540b86f6ea0SPaul Kocialkowski 	spin_unlock_irqrestore(&state->lock, flags);
541b86f6ea0SPaul Kocialkowski }
542b86f6ea0SPaul Kocialkowski 
sun6i_csi_capture_sync(struct sun6i_csi_device * csi_dev)543b86f6ea0SPaul Kocialkowski void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev)
544b86f6ea0SPaul Kocialkowski {
545b86f6ea0SPaul Kocialkowski 	sun6i_csi_capture_state_complete(csi_dev);
546b86f6ea0SPaul Kocialkowski 	sun6i_csi_capture_state_update(csi_dev);
547b86f6ea0SPaul Kocialkowski }
548b86f6ea0SPaul Kocialkowski 
549e9201cb2SPaul Kocialkowski /* Queue */
550e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_queue_setup(struct vb2_queue * queue,unsigned int * buffers_count,unsigned int * planes_count,unsigned int sizes[],struct device * alloc_devs[])551e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,
552e9201cb2SPaul Kocialkowski 					 unsigned int *buffers_count,
553e9201cb2SPaul Kocialkowski 					 unsigned int *planes_count,
554e9201cb2SPaul Kocialkowski 					 unsigned int sizes[],
555e9201cb2SPaul Kocialkowski 					 struct device *alloc_devs[])
556e9201cb2SPaul Kocialkowski {
557e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
558b86f6ea0SPaul Kocialkowski 	unsigned int size = csi_dev->capture.format.fmt.pix.sizeimage;
559e9201cb2SPaul Kocialkowski 
560e9201cb2SPaul Kocialkowski 	if (*planes_count)
561e9201cb2SPaul Kocialkowski 		return sizes[0] < size ? -EINVAL : 0;
562e9201cb2SPaul Kocialkowski 
563e9201cb2SPaul Kocialkowski 	*planes_count = 1;
564e9201cb2SPaul Kocialkowski 	sizes[0] = size;
565e9201cb2SPaul Kocialkowski 
566e9201cb2SPaul Kocialkowski 	return 0;
567e9201cb2SPaul Kocialkowski }
568e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_buffer_prepare(struct vb2_buffer * buffer)569e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
570e9201cb2SPaul Kocialkowski {
571e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
572e9201cb2SPaul Kocialkowski 	struct sun6i_csi_capture *capture = &csi_dev->capture;
573131823c4SPaul Kocialkowski 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
574e9201cb2SPaul Kocialkowski 	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
575e9201cb2SPaul Kocialkowski 	unsigned long size = capture->format.fmt.pix.sizeimage;
576e9201cb2SPaul Kocialkowski 
577e9201cb2SPaul Kocialkowski 	if (vb2_plane_size(buffer, 0) < size) {
578e9201cb2SPaul Kocialkowski 		v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",
579e9201cb2SPaul Kocialkowski 			 vb2_plane_size(buffer, 0), size);
580e9201cb2SPaul Kocialkowski 		return -EINVAL;
581e9201cb2SPaul Kocialkowski 	}
582e9201cb2SPaul Kocialkowski 
583e9201cb2SPaul Kocialkowski 	vb2_set_plane_payload(buffer, 0, size);
584e9201cb2SPaul Kocialkowski 
585e9201cb2SPaul Kocialkowski 	v4l2_buffer->field = capture->format.fmt.pix.field;
586e9201cb2SPaul Kocialkowski 
587e9201cb2SPaul Kocialkowski 	return 0;
588e9201cb2SPaul Kocialkowski }
589e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_buffer_queue(struct vb2_buffer * buffer)590e9201cb2SPaul Kocialkowski static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer)
591e9201cb2SPaul Kocialkowski {
592e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
593b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
594e9201cb2SPaul Kocialkowski 	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
595e9201cb2SPaul Kocialkowski 	struct sun6i_csi_buffer *csi_buffer =
596e9201cb2SPaul Kocialkowski 		container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
597e9201cb2SPaul Kocialkowski 	unsigned long flags;
598e9201cb2SPaul Kocialkowski 
599b86f6ea0SPaul Kocialkowski 	spin_lock_irqsave(&state->lock, flags);
600b86f6ea0SPaul Kocialkowski 	list_add_tail(&csi_buffer->list, &state->queue);
601b86f6ea0SPaul Kocialkowski 	spin_unlock_irqrestore(&state->lock, flags);
602e9201cb2SPaul Kocialkowski }
603e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_start_streaming(struct vb2_queue * queue,unsigned int count)604e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
605e9201cb2SPaul Kocialkowski 					     unsigned int count)
606e9201cb2SPaul Kocialkowski {
607e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
608c55d9813SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
609c55d9813SPaul Kocialkowski 	struct video_device *video_dev = &csi_dev->capture.video_dev;
610e77c8f6fSPaul Kocialkowski 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
611e9201cb2SPaul Kocialkowski 	int ret;
612e9201cb2SPaul Kocialkowski 
613b86f6ea0SPaul Kocialkowski 	state->sequence = 0;
614e9201cb2SPaul Kocialkowski 
615e9201cb2SPaul Kocialkowski 	ret = video_device_pipeline_alloc_start(video_dev);
616e9201cb2SPaul Kocialkowski 	if (ret < 0)
617b86f6ea0SPaul Kocialkowski 		goto error_state;
618e9201cb2SPaul Kocialkowski 
6195d34d90fSPaul Kocialkowski 	state->streaming = true;
6205d34d90fSPaul Kocialkowski 
621e9201cb2SPaul Kocialkowski 	ret = v4l2_subdev_call(subdev, video, s_stream, 1);
622e9201cb2SPaul Kocialkowski 	if (ret && ret != -ENOIOCTLCMD)
6235d34d90fSPaul Kocialkowski 		goto error_streaming;
624e9201cb2SPaul Kocialkowski 
625e9201cb2SPaul Kocialkowski 	return 0;
626e9201cb2SPaul Kocialkowski 
6275d34d90fSPaul Kocialkowski error_streaming:
6285d34d90fSPaul Kocialkowski 	state->streaming = false;
6295d34d90fSPaul Kocialkowski 
630e9201cb2SPaul Kocialkowski 	video_device_pipeline_stop(video_dev);
631e9201cb2SPaul Kocialkowski 
632b86f6ea0SPaul Kocialkowski error_state:
633b86f6ea0SPaul Kocialkowski 	sun6i_csi_capture_state_cleanup(csi_dev, false);
634e9201cb2SPaul Kocialkowski 
635e9201cb2SPaul Kocialkowski 	return ret;
636e9201cb2SPaul Kocialkowski }
637e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_stop_streaming(struct vb2_queue * queue)638e9201cb2SPaul Kocialkowski static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)
639e9201cb2SPaul Kocialkowski {
640e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
6415d34d90fSPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
642c55d9813SPaul Kocialkowski 	struct video_device *video_dev = &csi_dev->capture.video_dev;
643e77c8f6fSPaul Kocialkowski 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
644e9201cb2SPaul Kocialkowski 
645e9201cb2SPaul Kocialkowski 	v4l2_subdev_call(subdev, video, s_stream, 0);
646e9201cb2SPaul Kocialkowski 
6475d34d90fSPaul Kocialkowski 	state->streaming = false;
6485d34d90fSPaul Kocialkowski 
649c55d9813SPaul Kocialkowski 	video_device_pipeline_stop(video_dev);
650e9201cb2SPaul Kocialkowski 
651b86f6ea0SPaul Kocialkowski 	sun6i_csi_capture_state_cleanup(csi_dev, true);
652e9201cb2SPaul Kocialkowski }
653e9201cb2SPaul Kocialkowski 
654e9201cb2SPaul Kocialkowski static const struct vb2_ops sun6i_csi_capture_queue_ops = {
655e9201cb2SPaul Kocialkowski 	.queue_setup		= sun6i_csi_capture_queue_setup,
656e9201cb2SPaul Kocialkowski 	.buf_prepare		= sun6i_csi_capture_buffer_prepare,
657e9201cb2SPaul Kocialkowski 	.buf_queue		= sun6i_csi_capture_buffer_queue,
658e9201cb2SPaul Kocialkowski 	.start_streaming	= sun6i_csi_capture_start_streaming,
659e9201cb2SPaul Kocialkowski 	.stop_streaming		= sun6i_csi_capture_stop_streaming,
660e9201cb2SPaul Kocialkowski 	.wait_prepare		= vb2_ops_wait_prepare,
661e9201cb2SPaul Kocialkowski 	.wait_finish		= vb2_ops_wait_finish,
662e9201cb2SPaul Kocialkowski };
663e9201cb2SPaul Kocialkowski 
664e9201cb2SPaul Kocialkowski /* V4L2 Device */
665e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_format_prepare(struct v4l2_format * format)666c2aad411SPaul Kocialkowski static void sun6i_csi_capture_format_prepare(struct v4l2_format *format)
667c2aad411SPaul Kocialkowski {
668c2aad411SPaul Kocialkowski 	struct v4l2_pix_format *pix_format = &format->fmt.pix;
669c2aad411SPaul Kocialkowski 	const struct v4l2_format_info *info;
670c2aad411SPaul Kocialkowski 	unsigned int width, height;
671c2aad411SPaul Kocialkowski 
672c2aad411SPaul Kocialkowski 	v4l_bound_align_image(&pix_format->width, SUN6I_CSI_CAPTURE_WIDTH_MIN,
673c2aad411SPaul Kocialkowski 			      SUN6I_CSI_CAPTURE_WIDTH_MAX, 1,
674c2aad411SPaul Kocialkowski 			      &pix_format->height, SUN6I_CSI_CAPTURE_HEIGHT_MIN,
675c2aad411SPaul Kocialkowski 			      SUN6I_CSI_CAPTURE_HEIGHT_MAX, 1, 0);
676c2aad411SPaul Kocialkowski 
67753fd3926SPaul Kocialkowski 	if (!sun6i_csi_capture_format_find(pix_format->pixelformat))
67853fd3926SPaul Kocialkowski 		pix_format->pixelformat =
67953fd3926SPaul Kocialkowski 			sun6i_csi_capture_formats[0].pixelformat;
680c2aad411SPaul Kocialkowski 
681c2aad411SPaul Kocialkowski 	width = pix_format->width;
682c2aad411SPaul Kocialkowski 	height = pix_format->height;
683c2aad411SPaul Kocialkowski 
684c2aad411SPaul Kocialkowski 	info = v4l2_format_info(pix_format->pixelformat);
685c2aad411SPaul Kocialkowski 
686c2aad411SPaul Kocialkowski 	switch (pix_format->pixelformat) {
687c2aad411SPaul Kocialkowski 	case V4L2_PIX_FMT_NV12_16L16:
688c2aad411SPaul Kocialkowski 		pix_format->bytesperline = width * 12 / 8;
689c2aad411SPaul Kocialkowski 		pix_format->sizeimage = pix_format->bytesperline * height;
690c2aad411SPaul Kocialkowski 		break;
691c2aad411SPaul Kocialkowski 	case V4L2_PIX_FMT_JPEG:
692c2aad411SPaul Kocialkowski 		pix_format->bytesperline = width;
693c2aad411SPaul Kocialkowski 		pix_format->sizeimage = pix_format->bytesperline * height;
694c2aad411SPaul Kocialkowski 		break;
695c2aad411SPaul Kocialkowski 	default:
696c2aad411SPaul Kocialkowski 		v4l2_fill_pixfmt(pix_format, pix_format->pixelformat,
697c2aad411SPaul Kocialkowski 				 width, height);
698c2aad411SPaul Kocialkowski 		break;
699c2aad411SPaul Kocialkowski 	}
700c2aad411SPaul Kocialkowski 
701c2aad411SPaul Kocialkowski 	if (pix_format->field == V4L2_FIELD_ANY)
702c2aad411SPaul Kocialkowski 		pix_format->field = V4L2_FIELD_NONE;
703c2aad411SPaul Kocialkowski 
704c2aad411SPaul Kocialkowski 	if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG)
705c2aad411SPaul Kocialkowski 		pix_format->colorspace = V4L2_COLORSPACE_JPEG;
706c2aad411SPaul Kocialkowski 	else if (info && info->pixel_enc == V4L2_PIXEL_ENC_BAYER)
707c2aad411SPaul Kocialkowski 		pix_format->colorspace = V4L2_COLORSPACE_RAW;
708c2aad411SPaul Kocialkowski 	else
709c2aad411SPaul Kocialkowski 		pix_format->colorspace = V4L2_COLORSPACE_SRGB;
710c2aad411SPaul Kocialkowski 
711c2aad411SPaul Kocialkowski 	pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
712c2aad411SPaul Kocialkowski 	pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;
713c2aad411SPaul Kocialkowski 	pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
714c2aad411SPaul Kocialkowski }
715c2aad411SPaul Kocialkowski 
sun6i_csi_capture_querycap(struct file * file,void * private,struct v4l2_capability * capability)716e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_querycap(struct file *file, void *private,
717e9201cb2SPaul Kocialkowski 				      struct v4l2_capability *capability)
718e9201cb2SPaul Kocialkowski {
719e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
720e9201cb2SPaul Kocialkowski 	struct video_device *video_dev = &csi_dev->capture.video_dev;
721e9201cb2SPaul Kocialkowski 
722e9201cb2SPaul Kocialkowski 	strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver));
723e9201cb2SPaul Kocialkowski 	strscpy(capability->card, video_dev->name, sizeof(capability->card));
724e9201cb2SPaul Kocialkowski 	snprintf(capability->bus_info, sizeof(capability->bus_info),
725e9201cb2SPaul Kocialkowski 		 "platform:%s", dev_name(csi_dev->dev));
726e9201cb2SPaul Kocialkowski 
727e9201cb2SPaul Kocialkowski 	return 0;
728e9201cb2SPaul Kocialkowski }
729e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_enum_fmt(struct file * file,void * private,struct v4l2_fmtdesc * fmtdesc)730e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_enum_fmt(struct file *file, void *private,
731e9201cb2SPaul Kocialkowski 				      struct v4l2_fmtdesc *fmtdesc)
732e9201cb2SPaul Kocialkowski {
733e9201cb2SPaul Kocialkowski 	u32 index = fmtdesc->index;
734e9201cb2SPaul Kocialkowski 
735e9201cb2SPaul Kocialkowski 	if (index >= ARRAY_SIZE(sun6i_csi_capture_formats))
736e9201cb2SPaul Kocialkowski 		return -EINVAL;
737e9201cb2SPaul Kocialkowski 
73853fd3926SPaul Kocialkowski 	fmtdesc->pixelformat = sun6i_csi_capture_formats[index].pixelformat;
739e9201cb2SPaul Kocialkowski 
740e9201cb2SPaul Kocialkowski 	return 0;
741e9201cb2SPaul Kocialkowski }
742e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_g_fmt(struct file * file,void * private,struct v4l2_format * format)743e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_g_fmt(struct file *file, void *private,
744e9201cb2SPaul Kocialkowski 				   struct v4l2_format *format)
745e9201cb2SPaul Kocialkowski {
746e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
747e9201cb2SPaul Kocialkowski 
748c2aad411SPaul Kocialkowski 	*format = csi_dev->capture.format;
749e9201cb2SPaul Kocialkowski 
750e9201cb2SPaul Kocialkowski 	return 0;
751e9201cb2SPaul Kocialkowski }
752e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_s_fmt(struct file * file,void * private,struct v4l2_format * format)753e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_s_fmt(struct file *file, void *private,
754e9201cb2SPaul Kocialkowski 				   struct v4l2_format *format)
755e9201cb2SPaul Kocialkowski {
756e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
757e9201cb2SPaul Kocialkowski 	struct sun6i_csi_capture *capture = &csi_dev->capture;
758e9201cb2SPaul Kocialkowski 
759e9201cb2SPaul Kocialkowski 	if (vb2_is_busy(&capture->queue))
760e9201cb2SPaul Kocialkowski 		return -EBUSY;
761e9201cb2SPaul Kocialkowski 
762c2aad411SPaul Kocialkowski 	sun6i_csi_capture_format_prepare(format);
763c2aad411SPaul Kocialkowski 
764c2aad411SPaul Kocialkowski 	csi_dev->capture.format = *format;
765c2aad411SPaul Kocialkowski 
766c2aad411SPaul Kocialkowski 	return 0;
767e9201cb2SPaul Kocialkowski }
768e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_try_fmt(struct file * file,void * private,struct v4l2_format * format)769e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_try_fmt(struct file *file, void *private,
770e9201cb2SPaul Kocialkowski 				     struct v4l2_format *format)
771e9201cb2SPaul Kocialkowski {
772c2aad411SPaul Kocialkowski 	sun6i_csi_capture_format_prepare(format);
773e9201cb2SPaul Kocialkowski 
774c2aad411SPaul Kocialkowski 	return 0;
775e9201cb2SPaul Kocialkowski }
776e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_enum_input(struct file * file,void * private,struct v4l2_input * input)777e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_enum_input(struct file *file, void *private,
778e9201cb2SPaul Kocialkowski 					struct v4l2_input *input)
779e9201cb2SPaul Kocialkowski {
780e9201cb2SPaul Kocialkowski 	if (input->index != 0)
781e9201cb2SPaul Kocialkowski 		return -EINVAL;
782e9201cb2SPaul Kocialkowski 
783e9201cb2SPaul Kocialkowski 	input->type = V4L2_INPUT_TYPE_CAMERA;
784e9201cb2SPaul Kocialkowski 	strscpy(input->name, "Camera", sizeof(input->name));
785e9201cb2SPaul Kocialkowski 
786e9201cb2SPaul Kocialkowski 	return 0;
787e9201cb2SPaul Kocialkowski }
788e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_g_input(struct file * file,void * private,unsigned int * index)789e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_g_input(struct file *file, void *private,
790e9201cb2SPaul Kocialkowski 				     unsigned int *index)
791e9201cb2SPaul Kocialkowski {
792e9201cb2SPaul Kocialkowski 	*index = 0;
793e9201cb2SPaul Kocialkowski 
794e9201cb2SPaul Kocialkowski 	return 0;
795e9201cb2SPaul Kocialkowski }
796e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_s_input(struct file * file,void * private,unsigned int index)797e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_s_input(struct file *file, void *private,
798e9201cb2SPaul Kocialkowski 				     unsigned int index)
799e9201cb2SPaul Kocialkowski {
800e9201cb2SPaul Kocialkowski 	if (index != 0)
801e9201cb2SPaul Kocialkowski 		return -EINVAL;
802e9201cb2SPaul Kocialkowski 
803e9201cb2SPaul Kocialkowski 	return 0;
804e9201cb2SPaul Kocialkowski }
805e9201cb2SPaul Kocialkowski 
806e9201cb2SPaul Kocialkowski static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = {
807e9201cb2SPaul Kocialkowski 	.vidioc_querycap		= sun6i_csi_capture_querycap,
808e9201cb2SPaul Kocialkowski 
809e9201cb2SPaul Kocialkowski 	.vidioc_enum_fmt_vid_cap	= sun6i_csi_capture_enum_fmt,
810e9201cb2SPaul Kocialkowski 	.vidioc_g_fmt_vid_cap		= sun6i_csi_capture_g_fmt,
811e9201cb2SPaul Kocialkowski 	.vidioc_s_fmt_vid_cap		= sun6i_csi_capture_s_fmt,
812e9201cb2SPaul Kocialkowski 	.vidioc_try_fmt_vid_cap		= sun6i_csi_capture_try_fmt,
813e9201cb2SPaul Kocialkowski 
814e9201cb2SPaul Kocialkowski 	.vidioc_enum_input		= sun6i_csi_capture_enum_input,
815e9201cb2SPaul Kocialkowski 	.vidioc_g_input			= sun6i_csi_capture_g_input,
816e9201cb2SPaul Kocialkowski 	.vidioc_s_input			= sun6i_csi_capture_s_input,
817e9201cb2SPaul Kocialkowski 
818e9201cb2SPaul Kocialkowski 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
819e9201cb2SPaul Kocialkowski 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
820e9201cb2SPaul Kocialkowski 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
821e9201cb2SPaul Kocialkowski 	.vidioc_querybuf		= vb2_ioctl_querybuf,
822e9201cb2SPaul Kocialkowski 	.vidioc_expbuf			= vb2_ioctl_expbuf,
823e9201cb2SPaul Kocialkowski 	.vidioc_qbuf			= vb2_ioctl_qbuf,
824e9201cb2SPaul Kocialkowski 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
825e9201cb2SPaul Kocialkowski 	.vidioc_streamon		= vb2_ioctl_streamon,
826e9201cb2SPaul Kocialkowski 	.vidioc_streamoff		= vb2_ioctl_streamoff,
827e9201cb2SPaul Kocialkowski };
828e9201cb2SPaul Kocialkowski 
829e9201cb2SPaul Kocialkowski /* V4L2 File */
830e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_open(struct file * file)831e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_open(struct file *file)
832e9201cb2SPaul Kocialkowski {
833e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
834e9201cb2SPaul Kocialkowski 	struct sun6i_csi_capture *capture = &csi_dev->capture;
835*6ceef054SPaul Kocialkowski 	int ret;
836e9201cb2SPaul Kocialkowski 
837e9201cb2SPaul Kocialkowski 	if (mutex_lock_interruptible(&capture->lock))
838e9201cb2SPaul Kocialkowski 		return -ERESTARTSYS;
839e9201cb2SPaul Kocialkowski 
840d0895e0fSPaul Kocialkowski 	ret = v4l2_pipeline_pm_get(&capture->video_dev.entity);
841e9201cb2SPaul Kocialkowski 	if (ret < 0)
842e9201cb2SPaul Kocialkowski 		goto error_lock;
843e9201cb2SPaul Kocialkowski 
844d0895e0fSPaul Kocialkowski 	ret = v4l2_fh_open(file);
845e9201cb2SPaul Kocialkowski 	if (ret < 0)
846d0895e0fSPaul Kocialkowski 		goto error_pipeline;
847e9201cb2SPaul Kocialkowski 
848e9201cb2SPaul Kocialkowski 	mutex_unlock(&capture->lock);
849e9201cb2SPaul Kocialkowski 
850e9201cb2SPaul Kocialkowski 	return 0;
851e9201cb2SPaul Kocialkowski 
852d0895e0fSPaul Kocialkowski error_pipeline:
853d0895e0fSPaul Kocialkowski 	v4l2_pipeline_pm_put(&capture->video_dev.entity);
854e9201cb2SPaul Kocialkowski 
855e9201cb2SPaul Kocialkowski error_lock:
856e9201cb2SPaul Kocialkowski 	mutex_unlock(&capture->lock);
857e9201cb2SPaul Kocialkowski 
858e9201cb2SPaul Kocialkowski 	return ret;
859e9201cb2SPaul Kocialkowski }
860e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_close(struct file * file)861e9201cb2SPaul Kocialkowski static int sun6i_csi_capture_close(struct file *file)
862e9201cb2SPaul Kocialkowski {
863e9201cb2SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = video_drvdata(file);
864e9201cb2SPaul Kocialkowski 	struct sun6i_csi_capture *capture = &csi_dev->capture;
865e9201cb2SPaul Kocialkowski 
866e9201cb2SPaul Kocialkowski 	mutex_lock(&capture->lock);
867e9201cb2SPaul Kocialkowski 
868e9201cb2SPaul Kocialkowski 	_vb2_fop_release(file, NULL);
869e9201cb2SPaul Kocialkowski 	v4l2_pipeline_pm_put(&capture->video_dev.entity);
870e9201cb2SPaul Kocialkowski 
871e9201cb2SPaul Kocialkowski 	mutex_unlock(&capture->lock);
872e9201cb2SPaul Kocialkowski 
873e9201cb2SPaul Kocialkowski 	return 0;
874e9201cb2SPaul Kocialkowski }
875e9201cb2SPaul Kocialkowski 
876e9201cb2SPaul Kocialkowski static const struct v4l2_file_operations sun6i_csi_capture_fops = {
877e9201cb2SPaul Kocialkowski 	.owner		= THIS_MODULE,
878e9201cb2SPaul Kocialkowski 	.open		= sun6i_csi_capture_open,
879e9201cb2SPaul Kocialkowski 	.release	= sun6i_csi_capture_close,
880e9201cb2SPaul Kocialkowski 	.unlocked_ioctl	= video_ioctl2,
881e9201cb2SPaul Kocialkowski 	.mmap		= vb2_fop_mmap,
882e9201cb2SPaul Kocialkowski 	.poll		= vb2_fop_poll
883e9201cb2SPaul Kocialkowski };
884e9201cb2SPaul Kocialkowski 
885e9201cb2SPaul Kocialkowski /* Media Entity */
886e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_link_validate(struct media_link * link)8871fd07a80SPaul Kocialkowski static int sun6i_csi_capture_link_validate(struct media_link *link)
888e9201cb2SPaul Kocialkowski {
8891fd07a80SPaul Kocialkowski 	struct video_device *video_dev =
8901fd07a80SPaul Kocialkowski 		media_entity_to_video_device(link->sink->entity);
8911fd07a80SPaul Kocialkowski 	struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);
892131823c4SPaul Kocialkowski 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
8931fd07a80SPaul Kocialkowski 	const struct sun6i_csi_capture_format *capture_format;
8941fd07a80SPaul Kocialkowski 	const struct sun6i_csi_bridge_format *bridge_format;
8951fd07a80SPaul Kocialkowski 	unsigned int capture_width, capture_height;
8961fd07a80SPaul Kocialkowski 	unsigned int bridge_width, bridge_height;
8971fd07a80SPaul Kocialkowski 	const struct v4l2_format_info *format_info;
8981fd07a80SPaul Kocialkowski 	u32 pixelformat, capture_field;
8991fd07a80SPaul Kocialkowski 	u32 mbus_code, bridge_field;
9001fd07a80SPaul Kocialkowski 	bool match;
901e9201cb2SPaul Kocialkowski 
9021fd07a80SPaul Kocialkowski 	sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);
903e9201cb2SPaul Kocialkowski 
9041fd07a80SPaul Kocialkowski 	sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field);
9051fd07a80SPaul Kocialkowski 	capture_format = sun6i_csi_capture_format_find(pixelformat);
9061fd07a80SPaul Kocialkowski 	if (WARN_ON(!capture_format))
9071fd07a80SPaul Kocialkowski 		return -EINVAL;
9081fd07a80SPaul Kocialkowski 
9091fd07a80SPaul Kocialkowski 	sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);
9101fd07a80SPaul Kocialkowski 
9111fd07a80SPaul Kocialkowski 	sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);
9121fd07a80SPaul Kocialkowski 	bridge_format = sun6i_csi_bridge_format_find(mbus_code);
9131fd07a80SPaul Kocialkowski 	if (WARN_ON(!bridge_format))
9141fd07a80SPaul Kocialkowski 		return -EINVAL;
9151fd07a80SPaul Kocialkowski 
9161fd07a80SPaul Kocialkowski 	/* No cropping/scaling is supported. */
9171fd07a80SPaul Kocialkowski 	if (capture_width != bridge_width || capture_height != bridge_height) {
9181fd07a80SPaul Kocialkowski 		v4l2_err(v4l2_dev,
9191fd07a80SPaul Kocialkowski 			 "invalid input/output dimensions: %ux%u/%ux%u\n",
9201fd07a80SPaul Kocialkowski 			 bridge_width, bridge_height, capture_width,
9211fd07a80SPaul Kocialkowski 			 capture_height);
922e9201cb2SPaul Kocialkowski 		return -EINVAL;
923e9201cb2SPaul Kocialkowski 	}
924e9201cb2SPaul Kocialkowski 
9251fd07a80SPaul Kocialkowski 	format_info = v4l2_format_info(pixelformat);
9261fd07a80SPaul Kocialkowski 	/* Some formats are not listed. */
9271fd07a80SPaul Kocialkowski 	if (!format_info)
9281fd07a80SPaul Kocialkowski 		return 0;
929e9201cb2SPaul Kocialkowski 
9301fd07a80SPaul Kocialkowski 	if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
9311fd07a80SPaul Kocialkowski 	    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
9321fd07a80SPaul Kocialkowski 		goto invalid;
9331fd07a80SPaul Kocialkowski 
9341fd07a80SPaul Kocialkowski 	if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB &&
9351fd07a80SPaul Kocialkowski 	    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
9361fd07a80SPaul Kocialkowski 		goto invalid;
9371fd07a80SPaul Kocialkowski 
9381fd07a80SPaul Kocialkowski 	if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
9391fd07a80SPaul Kocialkowski 		if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 &&
9401fd07a80SPaul Kocialkowski 		    bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422)
9411fd07a80SPaul Kocialkowski 			goto invalid;
9421fd07a80SPaul Kocialkowski 
9431fd07a80SPaul Kocialkowski 		/* YUV420 input can't produce YUV422 output. */
9441fd07a80SPaul Kocialkowski 		if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 &&
9451fd07a80SPaul Kocialkowski 		    format_info->vdiv == 1)
9461fd07a80SPaul Kocialkowski 			goto invalid;
947e9201cb2SPaul Kocialkowski 	}
948e9201cb2SPaul Kocialkowski 
9491fd07a80SPaul Kocialkowski 	/* With raw input mode, we need a 1:1 match between input and output. */
9501fd07a80SPaul Kocialkowski 	if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||
9511fd07a80SPaul Kocialkowski 	    capture_format->input_format_raw) {
9521fd07a80SPaul Kocialkowski 		match = sun6i_csi_capture_format_match(pixelformat, mbus_code);
9531fd07a80SPaul Kocialkowski 		if (!match)
9541fd07a80SPaul Kocialkowski 			goto invalid;
955e9201cb2SPaul Kocialkowski 	}
956e9201cb2SPaul Kocialkowski 
957e9201cb2SPaul Kocialkowski 	return 0;
9581fd07a80SPaul Kocialkowski 
9591fd07a80SPaul Kocialkowski invalid:
9601fd07a80SPaul Kocialkowski 	v4l2_err(v4l2_dev, "invalid input/output format combination\n");
9611fd07a80SPaul Kocialkowski 	return -EINVAL;
962e9201cb2SPaul Kocialkowski }
963e9201cb2SPaul Kocialkowski 
964e9201cb2SPaul Kocialkowski static const struct media_entity_operations sun6i_csi_capture_media_ops = {
965e9201cb2SPaul Kocialkowski 	.link_validate = sun6i_csi_capture_link_validate
966e9201cb2SPaul Kocialkowski };
967e9201cb2SPaul Kocialkowski 
968e9201cb2SPaul Kocialkowski /* Capture */
969e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_setup(struct sun6i_csi_device * csi_dev)970e9201cb2SPaul Kocialkowski int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
971e9201cb2SPaul Kocialkowski {
972e9201cb2SPaul Kocialkowski 	struct sun6i_csi_capture *capture = &csi_dev->capture;
973b86f6ea0SPaul Kocialkowski 	struct sun6i_csi_capture_state *state = &capture->state;
974131823c4SPaul Kocialkowski 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
975e9201cb2SPaul Kocialkowski 	struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;
976e9201cb2SPaul Kocialkowski 	struct video_device *video_dev = &capture->video_dev;
977e9201cb2SPaul Kocialkowski 	struct vb2_queue *queue = &capture->queue;
978e9201cb2SPaul Kocialkowski 	struct media_pad *pad = &capture->pad;
979c2aad411SPaul Kocialkowski 	struct v4l2_format *format = &csi_dev->capture.format;
980c2aad411SPaul Kocialkowski 	struct v4l2_pix_format *pix_format = &format->fmt.pix;
981e9201cb2SPaul Kocialkowski 	int ret;
982e9201cb2SPaul Kocialkowski 
983131823c4SPaul Kocialkowski 	/* This may happen with multiple bridge notifier bound calls. */
984131823c4SPaul Kocialkowski 	if (state->setup)
985131823c4SPaul Kocialkowski 		return 0;
986131823c4SPaul Kocialkowski 
987b86f6ea0SPaul Kocialkowski 	/* State */
988b86f6ea0SPaul Kocialkowski 
989b86f6ea0SPaul Kocialkowski 	INIT_LIST_HEAD(&state->queue);
990b86f6ea0SPaul Kocialkowski 	spin_lock_init(&state->lock);
991b86f6ea0SPaul Kocialkowski 
992e9201cb2SPaul Kocialkowski 	/* Media Entity */
993e9201cb2SPaul Kocialkowski 
994e9201cb2SPaul Kocialkowski 	video_dev->entity.ops = &sun6i_csi_capture_media_ops;
995e9201cb2SPaul Kocialkowski 
996e9201cb2SPaul Kocialkowski 	/* Media Pad */
997e9201cb2SPaul Kocialkowski 
998e9201cb2SPaul Kocialkowski 	pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
999e9201cb2SPaul Kocialkowski 
1000e9201cb2SPaul Kocialkowski 	ret = media_entity_pads_init(&video_dev->entity, 1, pad);
1001e9201cb2SPaul Kocialkowski 	if (ret < 0)
1002e9201cb2SPaul Kocialkowski 		return ret;
1003e9201cb2SPaul Kocialkowski 
1004e9201cb2SPaul Kocialkowski 	/* Queue */
1005e9201cb2SPaul Kocialkowski 
1006e9201cb2SPaul Kocialkowski 	mutex_init(&capture->lock);
1007e9201cb2SPaul Kocialkowski 
1008e9201cb2SPaul Kocialkowski 	queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1009e9201cb2SPaul Kocialkowski 	queue->io_modes = VB2_MMAP | VB2_DMABUF;
1010e9201cb2SPaul Kocialkowski 	queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);
1011e9201cb2SPaul Kocialkowski 	queue->ops = &sun6i_csi_capture_queue_ops;
1012e9201cb2SPaul Kocialkowski 	queue->mem_ops = &vb2_dma_contig_memops;
1013b86f6ea0SPaul Kocialkowski 	queue->min_buffers_needed = 2;
1014e9201cb2SPaul Kocialkowski 	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1015e9201cb2SPaul Kocialkowski 	queue->lock = &capture->lock;
1016e9201cb2SPaul Kocialkowski 	queue->dev = csi_dev->dev;
1017e9201cb2SPaul Kocialkowski 	queue->drv_priv = csi_dev;
1018e9201cb2SPaul Kocialkowski 
1019e9201cb2SPaul Kocialkowski 	ret = vb2_queue_init(queue);
1020e9201cb2SPaul Kocialkowski 	if (ret) {
1021e9201cb2SPaul Kocialkowski 		v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
1022e9201cb2SPaul Kocialkowski 		goto error_media_entity;
1023e9201cb2SPaul Kocialkowski 	}
1024e9201cb2SPaul Kocialkowski 
1025e9201cb2SPaul Kocialkowski 	/* V4L2 Format */
1026e9201cb2SPaul Kocialkowski 
1027c2aad411SPaul Kocialkowski 	format->type = queue->type;
102853fd3926SPaul Kocialkowski 	pix_format->pixelformat = sun6i_csi_capture_formats[0].pixelformat;
1029e9201cb2SPaul Kocialkowski 	pix_format->width = 1280;
1030e9201cb2SPaul Kocialkowski 	pix_format->height = 720;
1031e9201cb2SPaul Kocialkowski 	pix_format->field = V4L2_FIELD_NONE;
1032e9201cb2SPaul Kocialkowski 
1033c2aad411SPaul Kocialkowski 	sun6i_csi_capture_format_prepare(format);
1034e9201cb2SPaul Kocialkowski 
1035e9201cb2SPaul Kocialkowski 	/* Video Device */
1036e9201cb2SPaul Kocialkowski 
103728bfb418SPaul Kocialkowski 	strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME,
103828bfb418SPaul Kocialkowski 		sizeof(video_dev->name));
1039e9201cb2SPaul Kocialkowski 	video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
1040e9201cb2SPaul Kocialkowski 	video_dev->vfl_dir = VFL_DIR_RX;
1041e9201cb2SPaul Kocialkowski 	video_dev->release = video_device_release_empty;
1042e9201cb2SPaul Kocialkowski 	video_dev->fops = &sun6i_csi_capture_fops;
1043e9201cb2SPaul Kocialkowski 	video_dev->ioctl_ops = &sun6i_csi_capture_ioctl_ops;
1044e9201cb2SPaul Kocialkowski 	video_dev->v4l2_dev = v4l2_dev;
1045e9201cb2SPaul Kocialkowski 	video_dev->queue = queue;
1046e9201cb2SPaul Kocialkowski 	video_dev->lock = &capture->lock;
1047e9201cb2SPaul Kocialkowski 
1048e9201cb2SPaul Kocialkowski 	video_set_drvdata(video_dev, csi_dev);
1049e9201cb2SPaul Kocialkowski 
1050e9201cb2SPaul Kocialkowski 	ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
1051e9201cb2SPaul Kocialkowski 	if (ret < 0) {
1052e9201cb2SPaul Kocialkowski 		v4l2_err(v4l2_dev, "failed to register video device: %d\n",
1053e9201cb2SPaul Kocialkowski 			 ret);
1054e9201cb2SPaul Kocialkowski 		goto error_media_entity;
1055e9201cb2SPaul Kocialkowski 	}
1056e9201cb2SPaul Kocialkowski 
1057e9201cb2SPaul Kocialkowski 	/* Media Pad Link */
1058e9201cb2SPaul Kocialkowski 
1059e9201cb2SPaul Kocialkowski 	ret = media_create_pad_link(&bridge_subdev->entity,
1060e9201cb2SPaul Kocialkowski 				    SUN6I_CSI_BRIDGE_PAD_SOURCE,
1061e9201cb2SPaul Kocialkowski 				    &video_dev->entity, 0,
1062131823c4SPaul Kocialkowski 				    csi_dev->isp_available ? 0 :
1063e9201cb2SPaul Kocialkowski 				    MEDIA_LNK_FL_ENABLED |
1064e9201cb2SPaul Kocialkowski 				    MEDIA_LNK_FL_IMMUTABLE);
1065e9201cb2SPaul Kocialkowski 	if (ret < 0) {
1066e9201cb2SPaul Kocialkowski 		v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",
1067e9201cb2SPaul Kocialkowski 			 bridge_subdev->entity.name,
1068e9201cb2SPaul Kocialkowski 			 SUN6I_CSI_BRIDGE_PAD_SOURCE,
1069e9201cb2SPaul Kocialkowski 			 video_dev->entity.name, 0);
1070e9201cb2SPaul Kocialkowski 		goto error_video_device;
1071e9201cb2SPaul Kocialkowski 	}
1072e9201cb2SPaul Kocialkowski 
1073131823c4SPaul Kocialkowski 	state->setup = true;
1074131823c4SPaul Kocialkowski 
1075e9201cb2SPaul Kocialkowski 	return 0;
1076e9201cb2SPaul Kocialkowski 
1077e9201cb2SPaul Kocialkowski error_video_device:
1078e9201cb2SPaul Kocialkowski 	vb2_video_unregister_device(video_dev);
1079e9201cb2SPaul Kocialkowski 
1080e9201cb2SPaul Kocialkowski error_media_entity:
1081e9201cb2SPaul Kocialkowski 	media_entity_cleanup(&video_dev->entity);
1082e9201cb2SPaul Kocialkowski 
1083e9201cb2SPaul Kocialkowski 	mutex_destroy(&capture->lock);
1084e9201cb2SPaul Kocialkowski 
1085e9201cb2SPaul Kocialkowski 	return ret;
1086e9201cb2SPaul Kocialkowski }
1087e9201cb2SPaul Kocialkowski 
sun6i_csi_capture_cleanup(struct sun6i_csi_device * csi_dev)1088e9201cb2SPaul Kocialkowski void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev)
1089e9201cb2SPaul Kocialkowski {
1090e9201cb2SPaul Kocialkowski 	struct sun6i_csi_capture *capture = &csi_dev->capture;
1091e9201cb2SPaul Kocialkowski 	struct video_device *video_dev = &capture->video_dev;
1092e9201cb2SPaul Kocialkowski 
1093131823c4SPaul Kocialkowski 	/* This may happen if async registration failed to complete. */
1094131823c4SPaul Kocialkowski 	if (!capture->state.setup)
1095131823c4SPaul Kocialkowski 		return;
1096131823c4SPaul Kocialkowski 
1097e9201cb2SPaul Kocialkowski 	vb2_video_unregister_device(video_dev);
1098e9201cb2SPaul Kocialkowski 	media_entity_cleanup(&video_dev->entity);
1099e9201cb2SPaul Kocialkowski 	mutex_destroy(&capture->lock);
1100131823c4SPaul Kocialkowski 
1101131823c4SPaul Kocialkowski 	capture->state.setup = false;
1102e9201cb2SPaul Kocialkowski }
1103