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