xref: /linux/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
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_state(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_state_get_format(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_state_get_format(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_state_get_format(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 	.enum_mbus_code	= sun6i_csi_bridge_enum_mbus_code,
585 	.get_fmt	= sun6i_csi_bridge_get_fmt,
586 	.set_fmt	= sun6i_csi_bridge_set_fmt,
587 };
588 
589 static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = {
590 	.video	= &sun6i_csi_bridge_video_ops,
591 	.pad	= &sun6i_csi_bridge_pad_ops,
592 };
593 
594 static const struct v4l2_subdev_internal_ops sun6i_csi_bridge_internal_ops = {
595 	.init_state	= sun6i_csi_bridge_init_state,
596 };
597 
598 /* Media Entity */
599 
600 static const struct media_entity_operations sun6i_csi_bridge_entity_ops = {
601 	.link_validate	= v4l2_subdev_link_validate,
602 };
603 
604 /* V4L2 Async */
605 
606 static int sun6i_csi_bridge_link(struct sun6i_csi_device *csi_dev,
607 				 int sink_pad_index,
608 				 struct v4l2_subdev *remote_subdev,
609 				 bool enabled)
610 {
611 	struct device *dev = csi_dev->dev;
612 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
613 	struct media_entity *sink_entity = &subdev->entity;
614 	struct media_entity *source_entity = &remote_subdev->entity;
615 	int source_pad_index;
616 	int ret;
617 
618 	/* Get the first remote source pad. */
619 	ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode,
620 					  MEDIA_PAD_FL_SOURCE);
621 	if (ret < 0) {
622 		dev_err(dev, "missing source pad in external entity %s\n",
623 			source_entity->name);
624 		return -EINVAL;
625 	}
626 
627 	source_pad_index = ret;
628 
629 	dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name,
630 		source_pad_index, sink_entity->name, sink_pad_index);
631 
632 	ret = media_create_pad_link(source_entity, source_pad_index,
633 				    sink_entity, sink_pad_index,
634 				    enabled ? MEDIA_LNK_FL_ENABLED : 0);
635 	if (ret < 0) {
636 		dev_err(dev, "failed to create %s:%u -> %s:%u link\n",
637 			source_entity->name, source_pad_index,
638 			sink_entity->name, sink_pad_index);
639 		return ret;
640 	}
641 
642 	return 0;
643 }
644 
645 static int
646 sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
647 				struct v4l2_subdev *remote_subdev,
648 				struct v4l2_async_connection *async_subdev)
649 {
650 	struct sun6i_csi_device *csi_dev =
651 		container_of(notifier, struct sun6i_csi_device,
652 			     bridge.notifier);
653 	struct sun6i_csi_bridge_async_subdev *bridge_async_subdev =
654 		container_of(async_subdev, struct sun6i_csi_bridge_async_subdev,
655 			     async_subdev);
656 	struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
657 	struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
658 	bool enabled = false;
659 	int ret;
660 
661 	switch (source->endpoint.base.port) {
662 	case SUN6I_CSI_PORT_PARALLEL:
663 		enabled = true;
664 		break;
665 	case SUN6I_CSI_PORT_MIPI_CSI2:
666 		enabled = !bridge->source_parallel.expected;
667 		break;
668 	default:
669 		return -EINVAL;
670 	}
671 
672 	source->subdev = remote_subdev;
673 
674 	if (csi_dev->isp_available) {
675 		/*
676 		 * Hook to the first available remote subdev to get v4l2 and
677 		 * media devices and register the capture device then.
678 		 */
679 		ret = sun6i_csi_isp_complete(csi_dev, remote_subdev->v4l2_dev);
680 		if (ret)
681 			return ret;
682 	}
683 
684 	return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK,
685 				     remote_subdev, enabled);
686 }
687 
688 static int
689 sun6i_csi_bridge_notifier_complete(struct v4l2_async_notifier *notifier)
690 {
691 	struct sun6i_csi_device *csi_dev =
692 		container_of(notifier, struct sun6i_csi_device,
693 			     bridge.notifier);
694 	struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
695 
696 	if (csi_dev->isp_available)
697 		return 0;
698 
699 	return v4l2_device_register_subdev_nodes(v4l2_dev);
700 }
701 
702 static const struct v4l2_async_notifier_operations
703 sun6i_csi_bridge_notifier_ops = {
704 	.bound		= sun6i_csi_bridge_notifier_bound,
705 	.complete	= sun6i_csi_bridge_notifier_complete,
706 };
707 
708 /* Bridge */
709 
710 static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev,
711 					 struct sun6i_csi_bridge_source *source,
712 					 u32 port,
713 					 enum v4l2_mbus_type *bus_types)
714 {
715 	struct device *dev = csi_dev->dev;
716 	struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier;
717 	struct v4l2_fwnode_endpoint *endpoint = &source->endpoint;
718 	struct sun6i_csi_bridge_async_subdev *bridge_async_subdev;
719 	struct fwnode_handle *handle;
720 	int ret;
721 
722 	handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0);
723 	if (!handle)
724 		return -ENODEV;
725 
726 	ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
727 	if (ret)
728 		goto complete;
729 
730 	if (bus_types) {
731 		bool valid = false;
732 		unsigned int i;
733 
734 		for (i = 0; bus_types[i] != V4L2_MBUS_INVALID; i++) {
735 			if (endpoint->bus_type == bus_types[i]) {
736 				valid = true;
737 				break;
738 			}
739 		}
740 
741 		if (!valid) {
742 			dev_err(dev, "unsupported bus type for port %d\n",
743 				port);
744 			ret = -EINVAL;
745 			goto complete;
746 		}
747 	}
748 
749 	bridge_async_subdev =
750 		v4l2_async_nf_add_fwnode_remote(notifier, handle,
751 						struct
752 						sun6i_csi_bridge_async_subdev);
753 	if (IS_ERR(bridge_async_subdev)) {
754 		ret = PTR_ERR(bridge_async_subdev);
755 		goto complete;
756 	}
757 
758 	bridge_async_subdev->source = source;
759 
760 	source->expected = true;
761 
762 complete:
763 	fwnode_handle_put(handle);
764 
765 	return ret;
766 }
767 
768 int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
769 {
770 	struct device *dev = csi_dev->dev;
771 	struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
772 	struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
773 	struct v4l2_subdev *subdev = &bridge->subdev;
774 	struct v4l2_async_notifier *notifier = &bridge->notifier;
775 	struct media_pad *pads = bridge->pads;
776 	enum v4l2_mbus_type parallel_mbus_types[] = {
777 		V4L2_MBUS_PARALLEL,
778 		V4L2_MBUS_BT656,
779 		V4L2_MBUS_INVALID
780 	};
781 	int ret;
782 
783 	mutex_init(&bridge->lock);
784 
785 	/* V4L2 Subdev */
786 
787 	v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops);
788 	subdev->internal_ops = &sun6i_csi_bridge_internal_ops;
789 	strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name));
790 	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
791 	subdev->owner = THIS_MODULE;
792 	subdev->dev = dev;
793 
794 	v4l2_set_subdevdata(subdev, csi_dev);
795 
796 	/* Media Entity */
797 
798 	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
799 	subdev->entity.ops = &sun6i_csi_bridge_entity_ops;
800 
801 	/* Media Pads */
802 
803 	pads[SUN6I_CSI_BRIDGE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
804 	pads[SUN6I_CSI_BRIDGE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
805 						  MEDIA_PAD_FL_MUST_CONNECT;
806 
807 	ret = media_entity_pads_init(&subdev->entity,
808 				     SUN6I_CSI_BRIDGE_PAD_COUNT, pads);
809 	if (ret < 0)
810 		return ret;
811 
812 	/* V4L2 Subdev */
813 
814 	if (csi_dev->isp_available)
815 		ret = v4l2_async_register_subdev(subdev);
816 	else
817 		ret = v4l2_device_register_subdev(v4l2_dev, subdev);
818 
819 	if (ret) {
820 		dev_err(dev, "failed to register v4l2 subdev: %d\n", ret);
821 		goto error_media_entity;
822 	}
823 
824 	/* V4L2 Async */
825 
826 	if (csi_dev->isp_available)
827 		v4l2_async_subdev_nf_init(notifier, subdev);
828 	else
829 		v4l2_async_nf_init(notifier, v4l2_dev);
830 	notifier->ops = &sun6i_csi_bridge_notifier_ops;
831 
832 	sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel,
833 				      SUN6I_CSI_PORT_PARALLEL,
834 				      parallel_mbus_types);
835 	sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2,
836 				      SUN6I_CSI_PORT_MIPI_CSI2, NULL);
837 
838 	ret = v4l2_async_nf_register(notifier);
839 	if (ret) {
840 		dev_err(dev, "failed to register v4l2 async notifier: %d\n",
841 			ret);
842 		goto error_v4l2_async_notifier;
843 	}
844 
845 	return 0;
846 
847 error_v4l2_async_notifier:
848 	v4l2_async_nf_cleanup(notifier);
849 
850 	if (csi_dev->isp_available)
851 		v4l2_async_unregister_subdev(subdev);
852 	else
853 		v4l2_device_unregister_subdev(subdev);
854 
855 error_media_entity:
856 	media_entity_cleanup(&subdev->entity);
857 
858 	return ret;
859 }
860 
861 void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev)
862 {
863 	struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
864 	struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier;
865 
866 	v4l2_async_nf_unregister(notifier);
867 	v4l2_async_nf_cleanup(notifier);
868 
869 	v4l2_device_unregister_subdev(subdev);
870 
871 	media_entity_cleanup(&subdev->entity);
872 }
873