1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2013--2024 Intel Corporation
7 #include <linux/device.h>
8 #include <linux/minmax.h>
10 #include <media/media-entity.h>
11 #include <media/mipi-csi2.h>
12 #include <media/v4l2-ctrls.h>
13 #include <media/v4l2-subdev.h>
16 #include "ipu6-isys.h"
17 #include "ipu6-isys-subdev.h"
19 unsigned int ipu6_isys_mbus_code_to_bpp(u32 code
)
22 case MEDIA_BUS_FMT_RGB888_1X24
:
23 case MEDIA_BUS_FMT_META_24
:
25 case MEDIA_BUS_FMT_RGB565_1X16
:
26 case MEDIA_BUS_FMT_UYVY8_1X16
:
27 case MEDIA_BUS_FMT_YUYV8_1X16
:
28 case MEDIA_BUS_FMT_META_16
:
30 case MEDIA_BUS_FMT_SBGGR12_1X12
:
31 case MEDIA_BUS_FMT_SGBRG12_1X12
:
32 case MEDIA_BUS_FMT_SGRBG12_1X12
:
33 case MEDIA_BUS_FMT_SRGGB12_1X12
:
34 case MEDIA_BUS_FMT_META_12
:
36 case MEDIA_BUS_FMT_SBGGR10_1X10
:
37 case MEDIA_BUS_FMT_SGBRG10_1X10
:
38 case MEDIA_BUS_FMT_SGRBG10_1X10
:
39 case MEDIA_BUS_FMT_SRGGB10_1X10
:
40 case MEDIA_BUS_FMT_META_10
:
42 case MEDIA_BUS_FMT_SBGGR8_1X8
:
43 case MEDIA_BUS_FMT_SGBRG8_1X8
:
44 case MEDIA_BUS_FMT_SGRBG8_1X8
:
45 case MEDIA_BUS_FMT_SRGGB8_1X8
:
46 case MEDIA_BUS_FMT_META_8
:
54 unsigned int ipu6_isys_mbus_code_to_mipi(u32 code
)
57 case MEDIA_BUS_FMT_RGB565_1X16
:
58 return MIPI_CSI2_DT_RGB565
;
59 case MEDIA_BUS_FMT_RGB888_1X24
:
60 return MIPI_CSI2_DT_RGB888
;
61 case MEDIA_BUS_FMT_UYVY8_1X16
:
62 case MEDIA_BUS_FMT_YUYV8_1X16
:
63 return MIPI_CSI2_DT_YUV422_8B
;
64 case MEDIA_BUS_FMT_SBGGR16_1X16
:
65 case MEDIA_BUS_FMT_SGBRG16_1X16
:
66 case MEDIA_BUS_FMT_SGRBG16_1X16
:
67 case MEDIA_BUS_FMT_SRGGB16_1X16
:
68 return MIPI_CSI2_DT_RAW16
;
69 case MEDIA_BUS_FMT_SBGGR12_1X12
:
70 case MEDIA_BUS_FMT_SGBRG12_1X12
:
71 case MEDIA_BUS_FMT_SGRBG12_1X12
:
72 case MEDIA_BUS_FMT_SRGGB12_1X12
:
73 return MIPI_CSI2_DT_RAW12
;
74 case MEDIA_BUS_FMT_SBGGR10_1X10
:
75 case MEDIA_BUS_FMT_SGBRG10_1X10
:
76 case MEDIA_BUS_FMT_SGRBG10_1X10
:
77 case MEDIA_BUS_FMT_SRGGB10_1X10
:
78 return MIPI_CSI2_DT_RAW10
;
79 case MEDIA_BUS_FMT_SBGGR8_1X8
:
80 case MEDIA_BUS_FMT_SGBRG8_1X8
:
81 case MEDIA_BUS_FMT_SGRBG8_1X8
:
82 case MEDIA_BUS_FMT_SRGGB8_1X8
:
83 return MIPI_CSI2_DT_RAW8
;
85 /* return unavailable MIPI data type - 0x3f */
91 bool ipu6_isys_is_bayer_format(u32 code
)
93 switch (ipu6_isys_mbus_code_to_mipi(code
)) {
94 case MIPI_CSI2_DT_RAW8
:
95 case MIPI_CSI2_DT_RAW10
:
96 case MIPI_CSI2_DT_RAW12
:
97 case MIPI_CSI2_DT_RAW14
:
98 case MIPI_CSI2_DT_RAW16
:
99 case MIPI_CSI2_DT_RAW20
:
100 case MIPI_CSI2_DT_RAW24
:
101 case MIPI_CSI2_DT_RAW28
:
108 u32
ipu6_isys_convert_bayer_order(u32 code
, int x
, int y
)
110 static const u32 code_map
[] = {
111 MEDIA_BUS_FMT_SRGGB8_1X8
,
112 MEDIA_BUS_FMT_SGRBG8_1X8
,
113 MEDIA_BUS_FMT_SGBRG8_1X8
,
114 MEDIA_BUS_FMT_SBGGR8_1X8
,
115 MEDIA_BUS_FMT_SRGGB10_1X10
,
116 MEDIA_BUS_FMT_SGRBG10_1X10
,
117 MEDIA_BUS_FMT_SGBRG10_1X10
,
118 MEDIA_BUS_FMT_SBGGR10_1X10
,
119 MEDIA_BUS_FMT_SRGGB12_1X12
,
120 MEDIA_BUS_FMT_SGRBG12_1X12
,
121 MEDIA_BUS_FMT_SGBRG12_1X12
,
122 MEDIA_BUS_FMT_SBGGR12_1X12
,
123 MEDIA_BUS_FMT_SRGGB16_1X16
,
124 MEDIA_BUS_FMT_SGRBG16_1X16
,
125 MEDIA_BUS_FMT_SGBRG16_1X16
,
126 MEDIA_BUS_FMT_SBGGR16_1X16
,
130 for (i
= 0; i
< ARRAY_SIZE(code_map
); i
++)
131 if (code_map
[i
] == code
)
134 if (WARN_ON(i
== ARRAY_SIZE(code_map
)))
137 return code_map
[i
^ (((y
& 1) << 1) | (x
& 1))];
140 int ipu6_isys_subdev_set_fmt(struct v4l2_subdev
*sd
,
141 struct v4l2_subdev_state
*state
,
142 struct v4l2_subdev_format
*format
)
144 struct ipu6_isys_subdev
*asd
= to_ipu6_isys_subdev(sd
);
145 struct v4l2_mbus_framefmt
*fmt
;
146 struct v4l2_rect
*crop
;
147 u32 code
= asd
->supported_codes
[0];
148 u32 other_pad
, other_stream
;
152 /* No transcoding, source and sink formats must match. */
153 if ((sd
->entity
.pads
[format
->pad
].flags
& MEDIA_PAD_FL_SOURCE
) &&
154 sd
->entity
.num_pads
> 1)
155 return v4l2_subdev_get_fmt(sd
, state
, format
);
157 format
->format
.width
= clamp(format
->format
.width
, IPU6_ISYS_MIN_WIDTH
,
158 IPU6_ISYS_MAX_WIDTH
);
159 format
->format
.height
= clamp(format
->format
.height
,
160 IPU6_ISYS_MIN_HEIGHT
,
161 IPU6_ISYS_MAX_HEIGHT
);
163 for (i
= 0; asd
->supported_codes
[i
]; i
++) {
164 if (asd
->supported_codes
[i
] == format
->format
.code
) {
165 code
= asd
->supported_codes
[i
];
169 format
->format
.code
= code
;
170 format
->format
.field
= V4L2_FIELD_NONE
;
172 /* Store the format and propagate it to the source pad. */
173 fmt
= v4l2_subdev_state_get_format(state
, format
->pad
, format
->stream
);
177 *fmt
= format
->format
;
179 if (!(sd
->entity
.pads
[format
->pad
].flags
& MEDIA_PAD_FL_SINK
))
182 /* propagate format to following source pad */
183 fmt
= v4l2_subdev_state_get_opposite_stream_format(state
, format
->pad
,
188 *fmt
= format
->format
;
190 ret
= v4l2_subdev_routing_find_opposite_end(&state
->routing
,
198 crop
= v4l2_subdev_state_get_crop(state
, other_pad
, other_stream
);
202 crop
->width
= fmt
->width
;
203 crop
->height
= fmt
->height
;
208 int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev
*sd
,
209 struct v4l2_subdev_state
*state
,
210 struct v4l2_subdev_mbus_code_enum
*code
)
212 struct ipu6_isys_subdev
*asd
= to_ipu6_isys_subdev(sd
);
213 const u32
*supported_codes
= asd
->supported_codes
;
216 for (index
= 0; supported_codes
[index
]; index
++) {
217 if (index
== code
->index
) {
218 code
->code
= supported_codes
[index
];
226 static int subdev_set_routing(struct v4l2_subdev
*sd
,
227 struct v4l2_subdev_state
*state
,
228 struct v4l2_subdev_krouting
*routing
)
230 static const struct v4l2_mbus_framefmt format
= {
233 .code
= MEDIA_BUS_FMT_SGRBG10_1X10
,
234 .field
= V4L2_FIELD_NONE
,
238 ret
= v4l2_subdev_routing_validate(sd
, routing
,
239 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1
);
243 return v4l2_subdev_set_routing_with_fmt(sd
, state
, routing
, &format
);
246 int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev
*sd
, u32 pad
, u32 stream
,
247 struct v4l2_mbus_framefmt
*format
)
249 struct v4l2_mbus_framefmt
*fmt
;
250 struct v4l2_subdev_state
*state
;
255 state
= v4l2_subdev_lock_and_get_active_state(sd
);
256 fmt
= v4l2_subdev_state_get_format(state
, pad
, stream
);
259 v4l2_subdev_unlock_state(state
);
261 return fmt
? 0 : -EINVAL
;
264 int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev
*sd
, u32 pad
, u32 stream
,
265 struct v4l2_rect
*crop
)
267 struct v4l2_subdev_state
*state
;
268 struct v4l2_rect
*rect
;
273 state
= v4l2_subdev_lock_and_get_active_state(sd
);
274 rect
= v4l2_subdev_state_get_crop(state
, pad
, stream
);
277 v4l2_subdev_unlock_state(state
);
279 return rect
? 0 : -EINVAL
;
282 u32
ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev
*sd
, u32 pad
)
284 struct v4l2_subdev_state
*state
;
285 struct v4l2_subdev_route
*routes
;
287 u32 source_stream
= 0;
289 state
= v4l2_subdev_lock_and_get_active_state(sd
);
293 routes
= state
->routing
.routes
;
294 for (i
= 0; i
< state
->routing
.num_routes
; i
++) {
295 if (routes
[i
].source_pad
== pad
) {
296 source_stream
= routes
[i
].source_stream
;
301 v4l2_subdev_unlock_state(state
);
303 return source_stream
;
306 static int ipu6_isys_subdev_init_state(struct v4l2_subdev
*sd
,
307 struct v4l2_subdev_state
*state
)
309 struct v4l2_subdev_route route
= {
314 .flags
= V4L2_SUBDEV_ROUTE_FL_ACTIVE
,
316 struct v4l2_subdev_krouting routing
= {
321 return subdev_set_routing(sd
, state
, &routing
);
324 int ipu6_isys_subdev_set_routing(struct v4l2_subdev
*sd
,
325 struct v4l2_subdev_state
*state
,
326 enum v4l2_subdev_format_whence which
,
327 struct v4l2_subdev_krouting
*routing
)
329 return subdev_set_routing(sd
, state
, routing
);
332 static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops
= {
333 .init_state
= ipu6_isys_subdev_init_state
,
336 int ipu6_isys_subdev_init(struct ipu6_isys_subdev
*asd
,
337 const struct v4l2_subdev_ops
*ops
,
338 unsigned int nr_ctrls
,
339 unsigned int num_sink_pads
,
340 unsigned int num_source_pads
)
342 unsigned int num_pads
= num_sink_pads
+ num_source_pads
;
346 v4l2_subdev_init(&asd
->sd
, ops
);
348 asd
->sd
.flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
|
349 V4L2_SUBDEV_FL_HAS_EVENTS
|
350 V4L2_SUBDEV_FL_STREAMS
;
351 asd
->sd
.owner
= THIS_MODULE
;
352 asd
->sd
.dev
= &asd
->isys
->adev
->auxdev
.dev
;
353 asd
->sd
.entity
.function
= MEDIA_ENT_F_VID_IF_BRIDGE
;
354 asd
->sd
.internal_ops
= &ipu6_isys_subdev_internal_ops
;
356 asd
->pad
= devm_kcalloc(&asd
->isys
->adev
->auxdev
.dev
, num_pads
,
357 sizeof(*asd
->pad
), GFP_KERNEL
);
361 for (i
= 0; i
< num_sink_pads
; i
++)
362 asd
->pad
[i
].flags
= MEDIA_PAD_FL_SINK
|
363 MEDIA_PAD_FL_MUST_CONNECT
;
365 for (i
= num_sink_pads
; i
< num_pads
; i
++)
366 asd
->pad
[i
].flags
= MEDIA_PAD_FL_SOURCE
;
368 ret
= media_entity_pads_init(&asd
->sd
.entity
, num_pads
, asd
->pad
);
372 if (asd
->ctrl_init
) {
373 ret
= v4l2_ctrl_handler_init(&asd
->ctrl_handler
, nr_ctrls
);
375 goto out_media_entity_cleanup
;
377 asd
->ctrl_init(&asd
->sd
);
378 if (asd
->ctrl_handler
.error
) {
379 ret
= asd
->ctrl_handler
.error
;
380 goto out_v4l2_ctrl_handler_free
;
383 asd
->sd
.ctrl_handler
= &asd
->ctrl_handler
;
390 out_v4l2_ctrl_handler_free
:
391 v4l2_ctrl_handler_free(&asd
->ctrl_handler
);
393 out_media_entity_cleanup
:
394 media_entity_cleanup(&asd
->sd
.entity
);
399 void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev
*asd
)
401 media_entity_cleanup(&asd
->sd
.entity
);
402 v4l2_ctrl_handler_free(&asd
->ctrl_handler
);