xref: /linux/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c (revision fd7d598270724cc787982ea48bbe17ad383a8b7f)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021-2022 Bootlin
4  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
5  */
6 
7 #include <linux/pm_runtime.h>
8 #include <linux/regmap.h>
9 #include <media/v4l2-device.h>
10 #include <media/v4l2-fwnode.h>
11 
12 #include "sun6i_csi.h"
13 #include "sun6i_csi_bridge.h"
14 #include "sun6i_csi_reg.h"
15 
16 /* Helpers */
17 
18 void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev,
19 				 unsigned int *width, unsigned int *height)
20 {
21 	if (width)
22 		*width = csi_dev->bridge.mbus_format.width;
23 	if (height)
24 		*height = csi_dev->bridge.mbus_format.height;
25 }
26 
27 void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev,
28 			     u32 *mbus_code, u32 *field)
29 {
30 	if (mbus_code)
31 		*mbus_code = csi_dev->bridge.mbus_format.code;
32 	if (field)
33 		*field = csi_dev->bridge.mbus_format.field;
34 }
35 
36 /* Format */
37 
38 static const struct sun6i_csi_bridge_format sun6i_csi_bridge_formats[] = {
39 	/* Bayer */
40 	{
41 		.mbus_code		= MEDIA_BUS_FMT_SBGGR8_1X8,
42 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
43 	},
44 	{
45 		.mbus_code		= MEDIA_BUS_FMT_SGBRG8_1X8,
46 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
47 	},
48 	{
49 		.mbus_code		= MEDIA_BUS_FMT_SGRBG8_1X8,
50 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
51 	},
52 	{
53 		.mbus_code		= MEDIA_BUS_FMT_SRGGB8_1X8,
54 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
55 	},
56 	{
57 		.mbus_code		= MEDIA_BUS_FMT_SBGGR10_1X10,
58 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
59 	},
60 	{
61 		.mbus_code		= MEDIA_BUS_FMT_SGBRG10_1X10,
62 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
63 	},
64 	{
65 		.mbus_code		= MEDIA_BUS_FMT_SGRBG10_1X10,
66 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
67 	},
68 	{
69 		.mbus_code		= MEDIA_BUS_FMT_SRGGB10_1X10,
70 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
71 	},
72 	{
73 		.mbus_code		= MEDIA_BUS_FMT_SBGGR12_1X12,
74 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
75 	},
76 	{
77 		.mbus_code		= MEDIA_BUS_FMT_SGBRG12_1X12,
78 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
79 	},
80 	{
81 		.mbus_code		= MEDIA_BUS_FMT_SGRBG12_1X12,
82 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
83 	},
84 	{
85 		.mbus_code		= MEDIA_BUS_FMT_SRGGB12_1X12,
86 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
87 	},
88 	/* RGB */
89 	{
90 		.mbus_code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
91 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
92 	},
93 	{
94 		.mbus_code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
95 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
96 	},
97 	/* YUV422 */
98 	{
99 		.mbus_code		= MEDIA_BUS_FMT_YUYV8_2X8,
100 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
101 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
102 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
103 	},
104 	{
105 		.mbus_code		= MEDIA_BUS_FMT_UYVY8_2X8,
106 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
107 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
108 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
109 	},
110 	{
111 		.mbus_code		= MEDIA_BUS_FMT_YVYU8_2X8,
112 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
113 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
114 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
115 	},
116 	{
117 		.mbus_code		= MEDIA_BUS_FMT_UYVY8_2X8,
118 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
119 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
120 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
121 	},
122 	{
123 		.mbus_code		= MEDIA_BUS_FMT_VYUY8_2X8,
124 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
125 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
126 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
127 	},
128 	{
129 		.mbus_code		= MEDIA_BUS_FMT_YUYV8_1X16,
130 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
131 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
132 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
133 	},
134 	{
135 		.mbus_code		= MEDIA_BUS_FMT_UYVY8_1X16,
136 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
137 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
138 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
139 	},
140 	{
141 		.mbus_code		= MEDIA_BUS_FMT_YVYU8_1X16,
142 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
143 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
144 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
145 	},
146 	{
147 		.mbus_code		= MEDIA_BUS_FMT_UYVY8_1X16,
148 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
149 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
150 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
151 	},
152 	{
153 		.mbus_code		= MEDIA_BUS_FMT_VYUY8_1X16,
154 		.input_format		= SUN6I_CSI_INPUT_FMT_YUV422,
155 		.input_yuv_seq		= SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
156 		.input_yuv_seq_invert	= SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
157 	},
158 	/* Compressed */
159 	{
160 		.mbus_code		= MEDIA_BUS_FMT_JPEG_1X8,
161 		.input_format		= SUN6I_CSI_INPUT_FMT_RAW,
162 	},
163 };
164 
165 const struct sun6i_csi_bridge_format *
166 sun6i_csi_bridge_format_find(u32 mbus_code)
167 {
168 	unsigned int i;
169 
170 	for (i = 0; i < ARRAY_SIZE(sun6i_csi_bridge_formats); i++)
171 		if (sun6i_csi_bridge_formats[i].mbus_code == mbus_code)
172 			return &sun6i_csi_bridge_formats[i];
173 
174 	return NULL;
175 }
176 
177 /* Bridge */
178 
179 static void sun6i_csi_bridge_irq_enable(struct sun6i_csi_device *csi_dev)
180 {
181 	struct regmap *regmap = csi_dev->regmap;
182 
183 	regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG,
184 		     SUN6I_CSI_CH_INT_EN_VS |
185 		     SUN6I_CSI_CH_INT_EN_HB_OF |
186 		     SUN6I_CSI_CH_INT_EN_FIFO2_OF |
187 		     SUN6I_CSI_CH_INT_EN_FIFO1_OF |
188 		     SUN6I_CSI_CH_INT_EN_FIFO0_OF |
189 		     SUN6I_CSI_CH_INT_EN_FD |
190 		     SUN6I_CSI_CH_INT_EN_CD);
191 }
192 
193 static void sun6i_csi_bridge_irq_disable(struct sun6i_csi_device *csi_dev)
194 {
195 	struct regmap *regmap = csi_dev->regmap;
196 
197 	regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0);
198 }
199 
200 static void sun6i_csi_bridge_irq_clear(struct sun6i_csi_device *csi_dev)
201 {
202 	struct regmap *regmap = csi_dev->regmap;
203 
204 	regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0);
205 	regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG,
206 		     SUN6I_CSI_CH_INT_STA_CLEAR);
207 }
208 
209 static void sun6i_csi_bridge_enable(struct sun6i_csi_device *csi_dev)
210 {
211 	struct regmap *regmap = csi_dev->regmap;
212 
213 	regmap_update_bits(regmap, SUN6I_CSI_EN_REG, SUN6I_CSI_EN_CSI_EN,
214 			   SUN6I_CSI_EN_CSI_EN);
215 
216 	regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON,
217 			   SUN6I_CSI_CAP_VCAP_ON);
218 }
219 
220 static void sun6i_csi_bridge_disable(struct sun6i_csi_device *csi_dev)
221 {
222 	struct regmap *regmap = csi_dev->regmap;
223 
224 	regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON, 0);
225 	regmap_update_bits(regmap, SUN6I_CSI_EN_REG, SUN6I_CSI_EN_CSI_EN, 0);
226 }
227 
228 static void
229 sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev)
230 {
231 	struct device *dev = csi_dev->dev;
232 	struct regmap *regmap = csi_dev->regmap;
233 	struct v4l2_fwnode_endpoint *endpoint =
234 		&csi_dev->bridge.source_parallel.endpoint;
235 	unsigned char bus_width = endpoint->bus.parallel.bus_width;
236 	unsigned int flags = endpoint->bus.parallel.flags;
237 	u32 field;
238 	u32 value = SUN6I_CSI_IF_CFG_IF_CSI;
239 
240 	sun6i_csi_bridge_format(csi_dev, NULL, &field);
241 
242 	if (field == V4L2_FIELD_INTERLACED ||
243 	    field == V4L2_FIELD_INTERLACED_TB ||
244 	    field == V4L2_FIELD_INTERLACED_BT)
245 		value |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED |
246 			 SUN6I_CSI_IF_CFG_FIELD_DT_PCLK_SHIFT(1) |
247 			 SUN6I_CSI_IF_CFG_FIELD_DT_FIELD_VSYNC;
248 	else
249 		value |= SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE;
250 
251 	switch (endpoint->bus_type) {
252 	case V4L2_MBUS_PARALLEL:
253 		if (bus_width == 16)
254 			value |= SUN6I_CSI_IF_CFG_IF_CSI_YUV_COMBINED;
255 		else
256 			value |= SUN6I_CSI_IF_CFG_IF_CSI_YUV_RAW;
257 
258 		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
259 			value |= SUN6I_CSI_IF_CFG_FIELD_NEGATIVE;
260 		else
261 			value |= SUN6I_CSI_IF_CFG_FIELD_POSITIVE;
262 
263 		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
264 			value |= SUN6I_CSI_IF_CFG_VREF_POL_NEGATIVE;
265 		else
266 			value |= SUN6I_CSI_IF_CFG_VREF_POL_POSITIVE;
267 
268 		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
269 			value |= SUN6I_CSI_IF_CFG_HREF_POL_NEGATIVE;
270 		else
271 			value |= SUN6I_CSI_IF_CFG_HREF_POL_POSITIVE;
272 
273 		if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
274 			value |= SUN6I_CSI_IF_CFG_CLK_POL_RISING;
275 		else
276 			value |= SUN6I_CSI_IF_CFG_CLK_POL_FALLING;
277 		break;
278 	case V4L2_MBUS_BT656:
279 		if (bus_width == 16)
280 			value |= SUN6I_CSI_IF_CFG_IF_CSI_BT1120;
281 		else
282 			value |= SUN6I_CSI_IF_CFG_IF_CSI_BT656;
283 
284 		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
285 			value |= SUN6I_CSI_IF_CFG_FIELD_NEGATIVE;
286 		else
287 			value |= SUN6I_CSI_IF_CFG_FIELD_POSITIVE;
288 
289 		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
290 			value |= SUN6I_CSI_IF_CFG_CLK_POL_RISING;
291 		else
292 			value |= SUN6I_CSI_IF_CFG_CLK_POL_FALLING;
293 		break;
294 	default:
295 		dev_warn(dev, "unsupported bus type: %d\n", endpoint->bus_type);
296 		break;
297 	}
298 
299 	switch (bus_width) {
300 	case 8:
301 	/* 16-bit YUV formats use a doubled width in 8-bit mode. */
302 	case 16:
303 		value |= SUN6I_CSI_IF_CFG_DATA_WIDTH_8;
304 		break;
305 	case 10:
306 		value |= SUN6I_CSI_IF_CFG_DATA_WIDTH_10;
307 		break;
308 	case 12:
309 		value |= SUN6I_CSI_IF_CFG_DATA_WIDTH_12;
310 		break;
311 	default:
312 		dev_warn(dev, "unsupported bus width: %u\n", bus_width);
313 		break;
314 	}
315 
316 	regmap_write(regmap, SUN6I_CSI_IF_CFG_REG, value);
317 }
318 
319 static void
320 sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev)
321 {
322 	struct regmap *regmap = csi_dev->regmap;
323 	u32 value = SUN6I_CSI_IF_CFG_IF_MIPI;
324 	u32 field;
325 
326 	sun6i_csi_bridge_format(csi_dev, NULL, &field);
327 
328 	if (field == V4L2_FIELD_INTERLACED ||
329 	    field == V4L2_FIELD_INTERLACED_TB ||
330 	    field == V4L2_FIELD_INTERLACED_BT)
331 		value |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED;
332 	else
333 		value |= SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE;
334 
335 	regmap_write(regmap, SUN6I_CSI_IF_CFG_REG, value);
336 }
337 
338 static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev)
339 {
340 	struct regmap *regmap = csi_dev->regmap;
341 	bool capture_streaming = csi_dev->capture.state.streaming;
342 	const struct sun6i_csi_bridge_format *bridge_format;
343 	const struct sun6i_csi_capture_format *capture_format;
344 	u32 mbus_code, field, pixelformat;
345 	u8 input_format, input_yuv_seq, output_format;
346 	u32 value = 0;
347 
348 	sun6i_csi_bridge_format(csi_dev, &mbus_code, &field);
349 
350 	bridge_format = sun6i_csi_bridge_format_find(mbus_code);
351 	if (WARN_ON(!bridge_format))
352 		return;
353 
354 	input_format = bridge_format->input_format;
355 	input_yuv_seq = bridge_format->input_yuv_seq;
356 
357 	if (capture_streaming) {
358 		sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);
359 
360 		capture_format = sun6i_csi_capture_format_find(pixelformat);
361 		if (WARN_ON(!capture_format))
362 			return;
363 
364 		if (capture_format->input_format_raw)
365 			input_format = SUN6I_CSI_INPUT_FMT_RAW;
366 
367 		if (capture_format->input_yuv_seq_invert)
368 			input_yuv_seq = bridge_format->input_yuv_seq_invert;
369 
370 		if (field == V4L2_FIELD_INTERLACED ||
371 		    field == V4L2_FIELD_INTERLACED_TB ||
372 		    field == V4L2_FIELD_INTERLACED_BT)
373 			output_format = capture_format->output_format_field;
374 		else
375 			output_format = capture_format->output_format_frame;
376 
377 		value |= SUN6I_CSI_CH_CFG_OUTPUT_FMT(output_format);
378 	}
379 
380 	value |= SUN6I_CSI_CH_CFG_INPUT_FMT(input_format);
381 	value |= SUN6I_CSI_CH_CFG_INPUT_YUV_SEQ(input_yuv_seq);
382 
383 	if (field == V4L2_FIELD_TOP)
384 		value |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD0;
385 	else if (field == V4L2_FIELD_BOTTOM)
386 		value |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD1;
387 	else
388 		value |= SUN6I_CSI_CH_CFG_FIELD_SEL_EITHER;
389 
390 	regmap_write(regmap, SUN6I_CSI_CH_CFG_REG, value);
391 }
392 
393 static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev,
394 				       struct sun6i_csi_bridge_source *source)
395 {
396 	struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
397 
398 	if (source == &bridge->source_parallel)
399 		sun6i_csi_bridge_configure_parallel(csi_dev);
400 	else
401 		sun6i_csi_bridge_configure_mipi_csi2(csi_dev);
402 
403 	sun6i_csi_bridge_configure_format(csi_dev);
404 }
405 
406 /* V4L2 Subdev */
407 
408 static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
409 {
410 	struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
411 	struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
412 	struct media_pad *local_pad = &bridge->pads[SUN6I_CSI_BRIDGE_PAD_SINK];
413 	bool capture_streaming = csi_dev->capture.state.streaming;
414 	struct device *dev = csi_dev->dev;
415 	struct sun6i_csi_bridge_source *source;
416 	struct v4l2_subdev *source_subdev;
417 	struct media_pad *remote_pad;
418 	int ret;
419 
420 	/* Source */
421 
422 	remote_pad = media_pad_remote_pad_unique(local_pad);
423 	if (IS_ERR(remote_pad)) {
424 		dev_err(dev,
425 			"zero or more than a single source connected to the bridge\n");
426 		return PTR_ERR(remote_pad);
427 	}
428 
429 	source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity);
430 
431 	if (source_subdev == bridge->source_parallel.subdev)
432 		source = &bridge->source_parallel;
433 	else
434 		source = &bridge->source_mipi_csi2;
435 
436 	if (!on) {
437 		v4l2_subdev_call(source_subdev, video, s_stream, 0);
438 		ret = 0;
439 		goto disable;
440 	}
441 
442 	/* PM */
443 
444 	ret = pm_runtime_resume_and_get(dev);
445 	if (ret < 0)
446 		return ret;
447 
448 	/* Clear */
449 
450 	sun6i_csi_bridge_irq_clear(csi_dev);
451 
452 	/* Configure */
453 
454 	sun6i_csi_bridge_configure(csi_dev, source);
455 
456 	if (capture_streaming)
457 		sun6i_csi_capture_configure(csi_dev);
458 
459 	/* State Update */
460 
461 	if (capture_streaming)
462 		sun6i_csi_capture_state_update(csi_dev);
463 
464 	/* Enable */
465 
466 	if (capture_streaming)
467 		sun6i_csi_bridge_irq_enable(csi_dev);
468 
469 	sun6i_csi_bridge_enable(csi_dev);
470 
471 	ret = v4l2_subdev_call(source_subdev, video, s_stream, 1);
472 	if (ret && ret != -ENOIOCTLCMD)
473 		goto disable;
474 
475 	return 0;
476 
477 disable:
478 	if (capture_streaming)
479 		sun6i_csi_bridge_irq_disable(csi_dev);
480 
481 	sun6i_csi_bridge_disable(csi_dev);
482 
483 	pm_runtime_put(dev);
484 
485 	return ret;
486 }
487 
488 static const struct v4l2_subdev_video_ops sun6i_csi_bridge_video_ops = {
489 	.s_stream	= sun6i_csi_bridge_s_stream,
490 };
491 
492 static void
493 sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
494 {
495 	if (!sun6i_csi_bridge_format_find(mbus_format->code))
496 		mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
497 
498 	mbus_format->field = V4L2_FIELD_NONE;
499 	mbus_format->colorspace = V4L2_COLORSPACE_RAW;
500 	mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT;
501 	mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
502 }
503 
504 static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev,
505 				     struct v4l2_subdev_state *state)
506 {
507 	struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
508 	unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK;
509 	struct v4l2_mbus_framefmt *mbus_format =
510 		v4l2_subdev_get_try_format(subdev, state, pad);
511 	struct mutex *lock = &csi_dev->bridge.lock;
512 
513 	mutex_lock(lock);
514 
515 	mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
516 	mbus_format->width = 1280;
517 	mbus_format->height = 720;
518 
519 	sun6i_csi_bridge_mbus_format_prepare(mbus_format);
520 
521 	mutex_unlock(lock);
522 
523 	return 0;
524 }
525 
526 static int
527 sun6i_csi_bridge_enum_mbus_code(struct v4l2_subdev *subdev,
528 				struct v4l2_subdev_state *state,
529 				struct v4l2_subdev_mbus_code_enum *code_enum)
530 {
531 	if (code_enum->index >= ARRAY_SIZE(sun6i_csi_bridge_formats))
532 		return -EINVAL;
533 
534 	code_enum->code = sun6i_csi_bridge_formats[code_enum->index].mbus_code;
535 
536 	return 0;
537 }
538 
539 static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev,
540 				    struct v4l2_subdev_state *state,
541 				    struct v4l2_subdev_format *format)
542 {
543 	struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
544 	struct v4l2_mbus_framefmt *mbus_format = &format->format;
545 	struct mutex *lock = &csi_dev->bridge.lock;
546 
547 	mutex_lock(lock);
548 
549 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
550 		*mbus_format = *v4l2_subdev_get_try_format(subdev, state,
551 							   format->pad);
552 	else
553 		*mbus_format = csi_dev->bridge.mbus_format;
554 
555 	mutex_unlock(lock);
556 
557 	return 0;
558 }
559 
560 static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev,
561 				    struct v4l2_subdev_state *state,
562 				    struct v4l2_subdev_format *format)
563 {
564 	struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
565 	struct v4l2_mbus_framefmt *mbus_format = &format->format;
566 	struct mutex *lock = &csi_dev->bridge.lock;
567 
568 	mutex_lock(lock);
569 
570 	sun6i_csi_bridge_mbus_format_prepare(mbus_format);
571 
572 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
573 		*v4l2_subdev_get_try_format(subdev, state, format->pad) =
574 			*mbus_format;
575 	else
576 		csi_dev->bridge.mbus_format = *mbus_format;
577 
578 	mutex_unlock(lock);
579 
580 	return 0;
581 }
582 
583 static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = {
584 	.init_cfg	= sun6i_csi_bridge_init_cfg,
585 	.enum_mbus_code	= sun6i_csi_bridge_enum_mbus_code,
586 	.get_fmt	= sun6i_csi_bridge_get_fmt,
587 	.set_fmt	= sun6i_csi_bridge_set_fmt,
588 };
589 
590 static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = {
591 	.video	= &sun6i_csi_bridge_video_ops,
592 	.pad	= &sun6i_csi_bridge_pad_ops,
593 };
594 
595 /* Media Entity */
596 
597 static const struct media_entity_operations sun6i_csi_bridge_entity_ops = {
598 	.link_validate	= v4l2_subdev_link_validate,
599 };
600 
601 /* V4L2 Async */
602 
603 static int sun6i_csi_bridge_link(struct sun6i_csi_device *csi_dev,
604 				 int sink_pad_index,
605 				 struct v4l2_subdev *remote_subdev,
606 				 bool enabled)
607 {
608 	struct device *dev = csi_dev->dev;
609 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
610 	struct media_entity *sink_entity = &subdev->entity;
611 	struct media_entity *source_entity = &remote_subdev->entity;
612 	int source_pad_index;
613 	int ret;
614 
615 	/* Get the first remote source pad. */
616 	ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode,
617 					  MEDIA_PAD_FL_SOURCE);
618 	if (ret < 0) {
619 		dev_err(dev, "missing source pad in external entity %s\n",
620 			source_entity->name);
621 		return -EINVAL;
622 	}
623 
624 	source_pad_index = ret;
625 
626 	dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name,
627 		source_pad_index, sink_entity->name, sink_pad_index);
628 
629 	ret = media_create_pad_link(source_entity, source_pad_index,
630 				    sink_entity, sink_pad_index,
631 				    enabled ? MEDIA_LNK_FL_ENABLED : 0);
632 	if (ret < 0) {
633 		dev_err(dev, "failed to create %s:%u -> %s:%u link\n",
634 			source_entity->name, source_pad_index,
635 			sink_entity->name, sink_pad_index);
636 		return ret;
637 	}
638 
639 	return 0;
640 }
641 
642 static int
643 sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
644 				struct v4l2_subdev *remote_subdev,
645 				struct v4l2_async_connection *async_subdev)
646 {
647 	struct sun6i_csi_device *csi_dev =
648 		container_of(notifier, struct sun6i_csi_device,
649 			     bridge.notifier);
650 	struct sun6i_csi_bridge_async_subdev *bridge_async_subdev =
651 		container_of(async_subdev, struct sun6i_csi_bridge_async_subdev,
652 			     async_subdev);
653 	struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
654 	struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
655 	bool enabled = false;
656 	int ret;
657 
658 	switch (source->endpoint.base.port) {
659 	case SUN6I_CSI_PORT_PARALLEL:
660 		enabled = true;
661 		break;
662 	case SUN6I_CSI_PORT_MIPI_CSI2:
663 		enabled = !bridge->source_parallel.expected;
664 		break;
665 	default:
666 		return -EINVAL;
667 	}
668 
669 	source->subdev = remote_subdev;
670 
671 	if (csi_dev->isp_available) {
672 		/*
673 		 * Hook to the first available remote subdev to get v4l2 and
674 		 * media devices and register the capture device then.
675 		 */
676 		ret = sun6i_csi_isp_complete(csi_dev, remote_subdev->v4l2_dev);
677 		if (ret)
678 			return ret;
679 	}
680 
681 	return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK,
682 				     remote_subdev, enabled);
683 }
684 
685 static int
686 sun6i_csi_bridge_notifier_complete(struct v4l2_async_notifier *notifier)
687 {
688 	struct sun6i_csi_device *csi_dev =
689 		container_of(notifier, struct sun6i_csi_device,
690 			     bridge.notifier);
691 	struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
692 
693 	if (csi_dev->isp_available)
694 		return 0;
695 
696 	return v4l2_device_register_subdev_nodes(v4l2_dev);
697 }
698 
699 static const struct v4l2_async_notifier_operations
700 sun6i_csi_bridge_notifier_ops = {
701 	.bound		= sun6i_csi_bridge_notifier_bound,
702 	.complete	= sun6i_csi_bridge_notifier_complete,
703 };
704 
705 /* Bridge */
706 
707 static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev,
708 					 struct sun6i_csi_bridge_source *source,
709 					 u32 port,
710 					 enum v4l2_mbus_type *bus_types)
711 {
712 	struct device *dev = csi_dev->dev;
713 	struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier;
714 	struct v4l2_fwnode_endpoint *endpoint = &source->endpoint;
715 	struct sun6i_csi_bridge_async_subdev *bridge_async_subdev;
716 	struct fwnode_handle *handle;
717 	int ret;
718 
719 	handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0);
720 	if (!handle)
721 		return -ENODEV;
722 
723 	ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
724 	if (ret)
725 		goto complete;
726 
727 	if (bus_types) {
728 		bool valid = false;
729 		unsigned int i;
730 
731 		for (i = 0; bus_types[i] != V4L2_MBUS_INVALID; i++) {
732 			if (endpoint->bus_type == bus_types[i]) {
733 				valid = true;
734 				break;
735 			}
736 		}
737 
738 		if (!valid) {
739 			dev_err(dev, "unsupported bus type for port %d\n",
740 				port);
741 			ret = -EINVAL;
742 			goto complete;
743 		}
744 	}
745 
746 	bridge_async_subdev =
747 		v4l2_async_nf_add_fwnode_remote(notifier, handle,
748 						struct
749 						sun6i_csi_bridge_async_subdev);
750 	if (IS_ERR(bridge_async_subdev)) {
751 		ret = PTR_ERR(bridge_async_subdev);
752 		goto complete;
753 	}
754 
755 	bridge_async_subdev->source = source;
756 
757 	source->expected = true;
758 
759 complete:
760 	fwnode_handle_put(handle);
761 
762 	return ret;
763 }
764 
765 int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
766 {
767 	struct device *dev = csi_dev->dev;
768 	struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
769 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
770 	struct v4l2_subdev *subdev = &bridge->subdev;
771 	struct v4l2_async_notifier *notifier = &bridge->notifier;
772 	struct media_pad *pads = bridge->pads;
773 	enum v4l2_mbus_type parallel_mbus_types[] = {
774 		V4L2_MBUS_PARALLEL,
775 		V4L2_MBUS_BT656,
776 		V4L2_MBUS_INVALID
777 	};
778 	int ret;
779 
780 	mutex_init(&bridge->lock);
781 
782 	/* V4L2 Subdev */
783 
784 	v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops);
785 	strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name));
786 	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
787 	subdev->owner = THIS_MODULE;
788 	subdev->dev = dev;
789 
790 	v4l2_set_subdevdata(subdev, csi_dev);
791 
792 	/* Media Entity */
793 
794 	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
795 	subdev->entity.ops = &sun6i_csi_bridge_entity_ops;
796 
797 	/* Media Pads */
798 
799 	pads[SUN6I_CSI_BRIDGE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
800 	pads[SUN6I_CSI_BRIDGE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
801 						  MEDIA_PAD_FL_MUST_CONNECT;
802 
803 	ret = media_entity_pads_init(&subdev->entity,
804 				     SUN6I_CSI_BRIDGE_PAD_COUNT, pads);
805 	if (ret < 0)
806 		return ret;
807 
808 	/* V4L2 Subdev */
809 
810 	if (csi_dev->isp_available)
811 		ret = v4l2_async_register_subdev(subdev);
812 	else
813 		ret = v4l2_device_register_subdev(v4l2_dev, subdev);
814 
815 	if (ret) {
816 		dev_err(dev, "failed to register v4l2 subdev: %d\n", ret);
817 		goto error_media_entity;
818 	}
819 
820 	/* V4L2 Async */
821 
822 	if (csi_dev->isp_available)
823 		v4l2_async_subdev_nf_init(notifier, subdev);
824 	else
825 		v4l2_async_nf_init(notifier, v4l2_dev);
826 	notifier->ops = &sun6i_csi_bridge_notifier_ops;
827 
828 	sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel,
829 				      SUN6I_CSI_PORT_PARALLEL,
830 				      parallel_mbus_types);
831 	sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2,
832 				      SUN6I_CSI_PORT_MIPI_CSI2, NULL);
833 
834 	ret = v4l2_async_nf_register(notifier);
835 	if (ret) {
836 		dev_err(dev, "failed to register v4l2 async notifier: %d\n",
837 			ret);
838 		goto error_v4l2_async_notifier;
839 	}
840 
841 	return 0;
842 
843 error_v4l2_async_notifier:
844 	v4l2_async_nf_cleanup(notifier);
845 
846 	if (csi_dev->isp_available)
847 		v4l2_async_unregister_subdev(subdev);
848 	else
849 		v4l2_device_unregister_subdev(subdev);
850 
851 error_media_entity:
852 	media_entity_cleanup(&subdev->entity);
853 
854 	return ret;
855 }
856 
857 void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev)
858 {
859 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
860 	struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier;
861 
862 	v4l2_async_nf_unregister(notifier);
863 	v4l2_async_nf_cleanup(notifier);
864 
865 	v4l2_device_unregister_subdev(subdev);
866 
867 	media_entity_cleanup(&subdev->entity);
868 }
869