GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / media / platform / sunxi / sun6i-csi / sun6i_csi_bridge.c
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 }