media: v4l2-subdev: Rename .init_cfg() operation to .init_state()
[linux-2.6-block.git] / drivers / media / platform / sunxi / sun6i-csi / sun6i_csi_bridge.c
CommitLineData
0d2b746b
PK
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"
21818a29 14#include "sun6i_csi_reg.h"
0d2b746b 15
90449f42
PK
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
0d2b746b
PK
36/* Format */
37
21818a29 38static const struct sun6i_csi_bridge_format sun6i_csi_bridge_formats[] = {
0d2b746b 39 /* Bayer */
21818a29
PK
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 },
0d2b746b 88 /* RGB */
21818a29
PK
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 },
0d2b746b 97 /* YUV422 */
21818a29
PK
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 },
0d2b746b 158 /* Compressed */
21818a29
PK
159 {
160 .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
161 .input_format = SUN6I_CSI_INPUT_FMT_RAW,
162 },
0d2b746b
PK
163};
164
21818a29
PK
165const struct sun6i_csi_bridge_format *
166sun6i_csi_bridge_format_find(u32 mbus_code)
0d2b746b
PK
167{
168 unsigned int i;
169
21818a29
PK
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];
0d2b746b 173
21818a29 174 return NULL;
0d2b746b
PK
175}
176
c55d9813
PK
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
a617f33d 229sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev)
c55d9813
PK
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
a617f33d
PK
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
c55d9813
PK
338static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev)
339{
340 struct regmap *regmap = csi_dev->regmap;
5d34d90f 341 bool capture_streaming = csi_dev->capture.state.streaming;
c55d9813
PK
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
5d34d90f
PK
357 if (capture_streaming) {
358 sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);
c55d9813 359
5d34d90f
PK
360 capture_format = sun6i_csi_capture_format_find(pixelformat);
361 if (WARN_ON(!capture_format))
362 return;
c55d9813 363
5d34d90f
PK
364 if (capture_format->input_format_raw)
365 input_format = SUN6I_CSI_INPUT_FMT_RAW;
c55d9813 366
5d34d90f
PK
367 if (capture_format->input_yuv_seq_invert)
368 input_yuv_seq = bridge_format->input_yuv_seq_invert;
c55d9813 369
5d34d90f
PK
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 }
c55d9813 379
c55d9813
PK
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
a617f33d
PK
393static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev,
394 struct sun6i_csi_bridge_source *source)
c55d9813 395{
a617f33d
PK
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
c55d9813
PK
403 sun6i_csi_bridge_configure_format(csi_dev);
404}
405
0d2b746b
PK
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];
5d34d90f 413 bool capture_streaming = csi_dev->capture.state.streaming;
0d2b746b 414 struct device *dev = csi_dev->dev;
a617f33d 415 struct sun6i_csi_bridge_source *source;
0d2b746b
PK
416 struct v4l2_subdev *source_subdev;
417 struct media_pad *remote_pad;
bc0cd108 418 int ret;
0d2b746b
PK
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
a617f33d
PK
431 if (source_subdev == bridge->source_parallel.subdev)
432 source = &bridge->source_parallel;
433 else
434 source = &bridge->source_mipi_csi2;
435
0d2b746b
PK
436 if (!on) {
437 v4l2_subdev_call(source_subdev, video, s_stream, 0);
bc0cd108 438 ret = 0;
0d2b746b
PK
439 goto disable;
440 }
441
c55d9813
PK
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
a617f33d 454 sun6i_csi_bridge_configure(csi_dev, source);
5d34d90f
PK
455
456 if (capture_streaming)
457 sun6i_csi_capture_configure(csi_dev);
c55d9813
PK
458
459 /* State Update */
460
5d34d90f
PK
461 if (capture_streaming)
462 sun6i_csi_capture_state_update(csi_dev);
c55d9813
PK
463
464 /* Enable */
465
5d34d90f
PK
466 if (capture_streaming)
467 sun6i_csi_bridge_irq_enable(csi_dev);
468
c55d9813
PK
469 sun6i_csi_bridge_enable(csi_dev);
470
0d2b746b
PK
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:
5d34d90f
PK
478 if (capture_streaming)
479 sun6i_csi_bridge_irq_disable(csi_dev);
480
c55d9813
PK
481 sun6i_csi_bridge_disable(csi_dev);
482
483 pm_runtime_put(dev);
0d2b746b
PK
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{
21818a29
PK
495 if (!sun6i_csi_bridge_format_find(mbus_format->code))
496 mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
0d2b746b
PK
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
5755be5f
LP
504static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev,
505 struct v4l2_subdev_state *state)
0d2b746b
PK
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 =
bc0e8d91 510 v4l2_subdev_state_get_format(state, pad);
0d2b746b
PK
511 struct mutex *lock = &csi_dev->bridge.lock;
512
513 mutex_lock(lock);
514
21818a29 515 mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
0d2b746b
PK
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{
21818a29 531 if (code_enum->index >= ARRAY_SIZE(sun6i_csi_bridge_formats))
0d2b746b
PK
532 return -EINVAL;
533
21818a29 534 code_enum->code = sun6i_csi_bridge_formats[code_enum->index].mbus_code;
0d2b746b
PK
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)
bc0e8d91
SA
550 *mbus_format = *v4l2_subdev_state_get_format(state,
551 format->pad);
0d2b746b
PK
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)
bc0e8d91 573 *v4l2_subdev_state_get_format(state, format->pad) =
0d2b746b
PK
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 = {
0d2b746b
PK
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
bc0cd108 589static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = {
0d2b746b
PK
590 .video = &sun6i_csi_bridge_video_ops,
591 .pad = &sun6i_csi_bridge_pad_ops,
592};
593
5755be5f
LP
594static const struct v4l2_subdev_internal_ops sun6i_csi_bridge_internal_ops = {
595 .init_state = sun6i_csi_bridge_init_state,
596};
597
0d2b746b
PK
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,
adb2dcd5 648 struct v4l2_async_connection *async_subdev)
0d2b746b
PK
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);
a617f33d 656 struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
0d2b746b 657 struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
bc0cd108 658 bool enabled = false;
131823c4 659 int ret;
0d2b746b
PK
660
661 switch (source->endpoint.base.port) {
662 case SUN6I_CSI_PORT_PARALLEL:
663 enabled = true;
664 break;
a617f33d
PK
665 case SUN6I_CSI_PORT_MIPI_CSI2:
666 enabled = !bridge->source_parallel.expected;
667 break;
0d2b746b 668 default:
7fabed7a 669 return -EINVAL;
0d2b746b
PK
670 }
671
672 source->subdev = remote_subdev;
673
131823c4
PK
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
0d2b746b
PK
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
131823c4
PK
696 if (csi_dev->isp_available)
697 return 0;
698
0d2b746b
PK
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;
131823c4 772 struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
0d2b746b
PK
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);
5755be5f 788 subdev->internal_ops = &sun6i_csi_bridge_internal_ops;
0d2b746b
PK
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
131823c4
PK
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
0d2b746b
PK
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
b8ec754a
SA
826 if (csi_dev->isp_available)
827 v4l2_async_subdev_nf_init(notifier, subdev);
828 else
829 v4l2_async_nf_init(notifier, v4l2_dev);
0d2b746b
PK
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);
a617f33d
PK
835 sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2,
836 SUN6I_CSI_PORT_MIPI_CSI2, NULL);
0d2b746b 837
b8ec754a 838 ret = v4l2_async_nf_register(notifier);
0d2b746b
PK
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
131823c4
PK
850 if (csi_dev->isp_available)
851 v4l2_async_unregister_subdev(subdev);
852 else
853 v4l2_device_unregister_subdev(subdev);
0d2b746b
PK
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}