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
18void 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
27void 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
38static 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
165const struct sun6i_csi_bridge_format *
166sun6i_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
179static 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
193static 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
200static 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
209static 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
220static 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
228static void
229sun6i_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
319static void
320sun6i_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
338static 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
393static 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
408static 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
477disable:
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
488static const struct v4l2_subdev_video_ops sun6i_csi_bridge_video_ops = {
489	.s_stream	= sun6i_csi_bridge_s_stream,
490};
491
492static void
493sun6i_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
504static 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
526static int
527sun6i_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
539static 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
560static 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
583static 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
589static 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
594static 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
600static const struct media_entity_operations sun6i_csi_bridge_entity_ops = {
601	.link_validate	= v4l2_subdev_link_validate,
602};
603
604/* V4L2 Async */
605
606static 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
645static int
646sun6i_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
688static int
689sun6i_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
702static const struct v4l2_async_notifier_operations
703sun6i_csi_bridge_notifier_ops = {
704	.bound		= sun6i_csi_bridge_notifier_bound,
705	.complete	= sun6i_csi_bridge_notifier_complete,
706};
707
708/* Bridge */
709
710static 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
762complete:
763	fwnode_handle_put(handle);
764
765	return ret;
766}
767
768int 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
847error_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
855error_media_entity:
856	media_entity_cleanup(&subdev->entity);
857
858	return ret;
859}
860
861void 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