1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2014 - 2021 Intel Corporation
4 #include <linux/device.h>
5 #include <linux/module.h>
7 #include <media/ipu-isys.h>
8 #include <media/media-entity.h>
9 #include <media/v4l2-device.h>
14 #include "ipu-isys-csi2-be.h"
15 #include "ipu-isys-subdev.h"
16 #include "ipu-isys-video.h"
19 * Raw bayer format pixel order MUST BE MAINTAINED in groups of four codes.
20 * Otherwise pixel order calculation below WILL BREAK!
22 static const u32 csi2_be_soc_supported_codes_pad
[] = {
23 MEDIA_BUS_FMT_Y10_1X10
,
24 MEDIA_BUS_FMT_RGB565_1X16
,
25 MEDIA_BUS_FMT_RGB888_1X24
,
26 MEDIA_BUS_FMT_UYVY8_1X16
,
27 MEDIA_BUS_FMT_YUYV8_1X16
,
28 MEDIA_BUS_FMT_SBGGR12_1X12
,
29 MEDIA_BUS_FMT_SGBRG12_1X12
,
30 MEDIA_BUS_FMT_SGRBG12_1X12
,
31 MEDIA_BUS_FMT_SRGGB12_1X12
,
32 MEDIA_BUS_FMT_SBGGR10_1X10
,
33 MEDIA_BUS_FMT_SGBRG10_1X10
,
34 MEDIA_BUS_FMT_SGRBG10_1X10
,
35 MEDIA_BUS_FMT_SRGGB10_1X10
,
36 MEDIA_BUS_FMT_SBGGR8_1X8
,
37 MEDIA_BUS_FMT_SGBRG8_1X8
,
38 MEDIA_BUS_FMT_SGRBG8_1X8
,
39 MEDIA_BUS_FMT_SRGGB8_1X8
,
44 * Raw bayer format pixel order MUST BE MAINTAINED in groups of four codes.
45 * Otherwise pixel order calculation below WILL BREAK!
47 static const u32 csi2_be_soc_supported_raw_bayer_codes_pad
[] = {
48 MEDIA_BUS_FMT_SBGGR12_1X12
,
49 MEDIA_BUS_FMT_SGBRG12_1X12
,
50 MEDIA_BUS_FMT_SGRBG12_1X12
,
51 MEDIA_BUS_FMT_SRGGB12_1X12
,
52 MEDIA_BUS_FMT_SBGGR10_1X10
,
53 MEDIA_BUS_FMT_SGBRG10_1X10
,
54 MEDIA_BUS_FMT_SGRBG10_1X10
,
55 MEDIA_BUS_FMT_SRGGB10_1X10
,
56 MEDIA_BUS_FMT_SBGGR8_1X8
,
57 MEDIA_BUS_FMT_SGBRG8_1X8
,
58 MEDIA_BUS_FMT_SGRBG8_1X8
,
59 MEDIA_BUS_FMT_SRGGB8_1X8
,
63 static int get_supported_code_index(u32 code
)
67 for (i
= 0; csi2_be_soc_supported_raw_bayer_codes_pad
[i
]; i
++) {
68 if (csi2_be_soc_supported_raw_bayer_codes_pad
[i
] == code
)
74 static const u32
*csi2_be_soc_supported_codes
[NR_OF_CSI2_BE_SOC_PADS
];
76 static struct v4l2_subdev_internal_ops csi2_be_soc_sd_internal_ops
= {
77 .open
= ipu_isys_subdev_open
,
78 .close
= ipu_isys_subdev_close
,
81 static const struct v4l2_subdev_core_ops csi2_be_soc_sd_core_ops
= {
84 static const struct v4l2_ctrl_config compression_ctrl_cfg
= {
86 .id
= V4L2_CID_IPU_ISYS_COMPRESSION
,
87 .name
= "ISYS BE-SOC compression",
88 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
95 static int set_stream(struct v4l2_subdev
*sd
, int enable
)
100 static const struct v4l2_subdev_video_ops csi2_be_soc_sd_video_ops
= {
101 .s_stream
= set_stream
,
105 __subdev_link_validate(struct v4l2_subdev
*sd
, struct media_link
*link
,
106 struct v4l2_subdev_format
*source_fmt
,
107 struct v4l2_subdev_format
*sink_fmt
)
109 struct ipu_isys_pipeline
*ip
= container_of(sd
->entity
.pipe
,
110 struct ipu_isys_pipeline
,
113 ip
->csi2_be_soc
= to_ipu_isys_csi2_be_soc(sd
);
114 return ipu_isys_subdev_link_validate(sd
, link
, source_fmt
, sink_fmt
);
118 ipu_isys_csi2_be_soc_set_sel(struct v4l2_subdev
*sd
,
119 struct v4l2_subdev_pad_config
*cfg
,
120 struct v4l2_subdev_selection
*sel
)
122 struct ipu_isys_subdev
*asd
= to_ipu_isys_subdev(sd
);
123 struct media_pad
*pad
= &asd
->sd
.entity
.pads
[sel
->pad
];
125 if (sel
->target
== V4L2_SEL_TGT_CROP
&&
126 pad
->flags
& MEDIA_PAD_FL_SOURCE
&&
127 asd
->valid_tgts
[sel
->pad
].crop
) {
128 enum isys_subdev_prop_tgt tgt
=
129 IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP
;
130 struct v4l2_mbus_framefmt
*ffmt
=
131 __ipu_isys_get_ffmt(sd
, cfg
, sel
->pad
, sel
->which
);
133 if (get_supported_code_index(ffmt
->code
) < 0) {
134 /* Non-bayer formats can't be odd lines cropped */
139 sel
->r
.width
= clamp(sel
->r
.width
, IPU_ISYS_MIN_WIDTH
,
142 sel
->r
.height
= clamp(sel
->r
.height
, IPU_ISYS_MIN_HEIGHT
,
143 IPU_ISYS_MAX_HEIGHT
);
145 *__ipu_isys_get_selection(sd
, cfg
, sel
->target
, sel
->pad
,
146 sel
->which
) = sel
->r
;
147 ipu_isys_subdev_fmt_propagate(sd
, cfg
, NULL
, &sel
->r
,
148 tgt
, sel
->pad
, sel
->which
);
154 static const struct v4l2_subdev_pad_ops csi2_be_soc_sd_pad_ops
= {
155 .link_validate
= __subdev_link_validate
,
156 .get_fmt
= ipu_isys_subdev_get_ffmt
,
157 .set_fmt
= ipu_isys_subdev_set_ffmt
,
158 .get_selection
= ipu_isys_subdev_get_sel
,
159 .set_selection
= ipu_isys_csi2_be_soc_set_sel
,
160 .enum_mbus_code
= ipu_isys_subdev_enum_mbus_code
,
163 static struct v4l2_subdev_ops csi2_be_soc_sd_ops
= {
164 .core
= &csi2_be_soc_sd_core_ops
,
165 .video
= &csi2_be_soc_sd_video_ops
,
166 .pad
= &csi2_be_soc_sd_pad_ops
,
169 static struct media_entity_operations csi2_be_soc_entity_ops
= {
170 .link_validate
= v4l2_subdev_link_validate
,
173 static void csi2_be_soc_set_ffmt(struct v4l2_subdev
*sd
,
174 struct v4l2_subdev_pad_config
*cfg
,
175 struct v4l2_subdev_format
*fmt
)
177 struct v4l2_mbus_framefmt
*ffmt
=
178 __ipu_isys_get_ffmt(sd
, cfg
, fmt
->pad
,
181 if (sd
->entity
.pads
[fmt
->pad
].flags
& MEDIA_PAD_FL_SINK
) {
182 if (fmt
->format
.field
!= V4L2_FIELD_ALTERNATE
)
183 fmt
->format
.field
= V4L2_FIELD_NONE
;
186 ipu_isys_subdev_fmt_propagate(sd
, cfg
, &fmt
->format
,
188 IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT
,
189 fmt
->pad
, fmt
->which
);
190 } else if (sd
->entity
.pads
[fmt
->pad
].flags
& MEDIA_PAD_FL_SOURCE
) {
191 struct v4l2_mbus_framefmt
*sink_ffmt
;
192 struct v4l2_rect
*r
= __ipu_isys_get_selection(sd
, cfg
,
193 V4L2_SEL_TGT_CROP
, fmt
->pad
, fmt
->which
);
194 struct ipu_isys_subdev
*asd
= to_ipu_isys_subdev(sd
);
198 sink_ffmt
= __ipu_isys_get_ffmt(sd
, cfg
, 0, fmt
->which
);
199 code
= sink_ffmt
->code
;
200 idx
= get_supported_code_index(code
);
202 if (asd
->valid_tgts
[fmt
->pad
].crop
&& idx
>= 0) {
205 /* Only croping odd line at top side. */
207 crop_info
|= CSI2_BE_CROP_VER
;
209 code
= csi2_be_soc_supported_raw_bayer_codes_pad
210 [((idx
& CSI2_BE_CROP_MASK
) ^ crop_info
)
211 + (idx
& ~CSI2_BE_CROP_MASK
)];
215 ffmt
->width
= r
->width
;
216 ffmt
->height
= r
->height
;
217 ffmt
->field
= sink_ffmt
->field
;
222 void ipu_isys_csi2_be_soc_cleanup(struct ipu_isys_csi2_be_soc
*csi2_be_soc
)
224 v4l2_device_unregister_subdev(&csi2_be_soc
->asd
.sd
);
225 ipu_isys_subdev_cleanup(&csi2_be_soc
->asd
);
226 v4l2_ctrl_handler_free(&csi2_be_soc
->av
.ctrl_handler
);
227 ipu_isys_video_cleanup(&csi2_be_soc
->av
);
230 int ipu_isys_csi2_be_soc_init(struct ipu_isys_csi2_be_soc
*csi2_be_soc
,
231 struct ipu_isys
*isys
, int index
)
233 struct v4l2_subdev_format fmt
= {
234 .which
= V4L2_SUBDEV_FORMAT_ACTIVE
,
235 .pad
= CSI2_BE_SOC_PAD_SINK
,
243 csi2_be_soc
->asd
.sd
.entity
.ops
= &csi2_be_soc_entity_ops
;
244 csi2_be_soc
->asd
.isys
= isys
;
246 rval
= ipu_isys_subdev_init(&csi2_be_soc
->asd
,
247 &csi2_be_soc_sd_ops
, 0,
248 NR_OF_CSI2_BE_SOC_PADS
,
249 NR_OF_CSI2_BE_SOC_SOURCE_PADS
,
250 NR_OF_CSI2_BE_SOC_SINK_PADS
, 0);
254 csi2_be_soc
->asd
.pad
[CSI2_BE_SOC_PAD_SINK
].flags
= MEDIA_PAD_FL_SINK
;
255 csi2_be_soc
->asd
.pad
[CSI2_BE_SOC_PAD_SOURCE
].flags
=
257 csi2_be_soc
->asd
.valid_tgts
[CSI2_BE_SOC_PAD_SOURCE
].crop
= true;
259 for (i
= 0; i
< NR_OF_CSI2_BE_SOC_PADS
; i
++)
260 csi2_be_soc_supported_codes
[i
] =
261 csi2_be_soc_supported_codes_pad
;
262 csi2_be_soc
->asd
.supported_codes
= csi2_be_soc_supported_codes
;
263 csi2_be_soc
->asd
.be_mode
= IPU_BE_SOC
;
264 csi2_be_soc
->asd
.isl_mode
= IPU_ISL_OFF
;
265 csi2_be_soc
->asd
.set_ffmt
= csi2_be_soc_set_ffmt
;
267 fmt
.pad
= CSI2_BE_SOC_PAD_SINK
;
268 ipu_isys_subdev_set_ffmt(&csi2_be_soc
->asd
.sd
, NULL
, &fmt
);
269 fmt
.pad
= CSI2_BE_SOC_PAD_SOURCE
;
270 ipu_isys_subdev_set_ffmt(&csi2_be_soc
->asd
.sd
, NULL
, &fmt
);
271 csi2_be_soc
->asd
.sd
.internal_ops
= &csi2_be_soc_sd_internal_ops
;
273 snprintf(csi2_be_soc
->asd
.sd
.name
, sizeof(csi2_be_soc
->asd
.sd
.name
),
274 IPU_ISYS_ENTITY_PREFIX
" CSI2 BE SOC %d", index
);
275 v4l2_set_subdevdata(&csi2_be_soc
->asd
.sd
, &csi2_be_soc
->asd
);
277 rval
= v4l2_device_register_subdev(&isys
->v4l2_dev
,
278 &csi2_be_soc
->asd
.sd
);
280 dev_info(&isys
->adev
->dev
, "can't register v4l2 subdev\n");
284 snprintf(csi2_be_soc
->av
.vdev
.name
, sizeof(csi2_be_soc
->av
.vdev
.name
),
285 IPU_ISYS_ENTITY_PREFIX
" BE SOC capture %d", index
);
288 * Pin type could be overwritten for YUV422 to I420 case, at
291 csi2_be_soc
->av
.aq
.css_pin_type
= IPU_FW_ISYS_PIN_TYPE_RAW_SOC
;
292 csi2_be_soc
->av
.isys
= isys
;
293 csi2_be_soc
->av
.pfmts
= ipu_isys_pfmts_be_soc
;
294 csi2_be_soc
->av
.try_fmt_vid_mplane
=
295 ipu_isys_video_try_fmt_vid_mplane_default
;
296 csi2_be_soc
->av
.prepare_fw_stream
=
297 ipu_isys_prepare_fw_cfg_default
;
298 csi2_be_soc
->av
.aq
.buf_prepare
= ipu_isys_buf_prepare
;
299 csi2_be_soc
->av
.aq
.fill_frame_buff_set_pin
=
300 ipu_isys_buffer_to_fw_frame_buff_pin
;
301 csi2_be_soc
->av
.aq
.link_fmt_validate
= ipu_isys_link_fmt_validate
;
302 csi2_be_soc
->av
.aq
.vbq
.buf_struct_size
=
303 sizeof(struct ipu_isys_video_buffer
);
305 /* create v4l2 ctrl for be-soc video node */
306 rval
= v4l2_ctrl_handler_init(&csi2_be_soc
->av
.ctrl_handler
, 0);
308 dev_err(&isys
->adev
->dev
,
309 "failed to init v4l2 ctrl handler for be_soc\n");
313 csi2_be_soc
->av
.compression_ctrl
=
314 v4l2_ctrl_new_custom(&csi2_be_soc
->av
.ctrl_handler
,
315 &compression_ctrl_cfg
, NULL
);
316 if (!csi2_be_soc
->av
.compression_ctrl
) {
317 dev_err(&isys
->adev
->dev
,
318 "failed to create BE-SOC cmprs ctrl\n");
321 csi2_be_soc
->av
.compression
= 0;
322 csi2_be_soc
->av
.vdev
.ctrl_handler
=
323 &csi2_be_soc
->av
.ctrl_handler
;
325 rval
= ipu_isys_video_init(&csi2_be_soc
->av
,
326 &csi2_be_soc
->asd
.sd
.entity
,
327 CSI2_BE_SOC_PAD_SOURCE
,
329 MEDIA_LNK_FL_DYNAMIC
);
331 dev_info(&isys
->adev
->dev
, "can't init video node\n");
338 ipu_isys_csi2_be_soc_cleanup(csi2_be_soc
);