1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2013 - 2025 Intel Corporation 4 */ 5 6 #include <linux/bug.h> 7 #include <linux/device.h> 8 #include <linux/minmax.h> 9 #include <linux/types.h> 10 11 #include <media/media-entity.h> 12 #include <media/mipi-csi2.h> 13 #include <media/v4l2-ctrls.h> 14 #include <media/v4l2-subdev.h> 15 16 #include <uapi/linux/media-bus-format.h> 17 18 #include "ipu7-bus.h" 19 #include "ipu7-isys.h" 20 #include "ipu7-isys-subdev.h" 21 22 unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) 23 { 24 switch (code) { 25 case MEDIA_BUS_FMT_RGB565_1X16: 26 return MIPI_CSI2_DT_RGB565; 27 case MEDIA_BUS_FMT_RGB888_1X24: 28 return MIPI_CSI2_DT_RGB888; 29 case MEDIA_BUS_FMT_YUYV10_1X20: 30 return MIPI_CSI2_DT_YUV422_10B; 31 case MEDIA_BUS_FMT_UYVY8_1X16: 32 case MEDIA_BUS_FMT_YUYV8_1X16: 33 return MIPI_CSI2_DT_YUV422_8B; 34 case MEDIA_BUS_FMT_SBGGR12_1X12: 35 case MEDIA_BUS_FMT_SGBRG12_1X12: 36 case MEDIA_BUS_FMT_SGRBG12_1X12: 37 case MEDIA_BUS_FMT_SRGGB12_1X12: 38 return MIPI_CSI2_DT_RAW12; 39 case MEDIA_BUS_FMT_Y10_1X10: 40 case MEDIA_BUS_FMT_SBGGR10_1X10: 41 case MEDIA_BUS_FMT_SGBRG10_1X10: 42 case MEDIA_BUS_FMT_SGRBG10_1X10: 43 case MEDIA_BUS_FMT_SRGGB10_1X10: 44 return MIPI_CSI2_DT_RAW10; 45 case MEDIA_BUS_FMT_SBGGR8_1X8: 46 case MEDIA_BUS_FMT_SGBRG8_1X8: 47 case MEDIA_BUS_FMT_SGRBG8_1X8: 48 case MEDIA_BUS_FMT_SRGGB8_1X8: 49 return MIPI_CSI2_DT_RAW8; 50 default: 51 WARN_ON(1); 52 return 0xff; 53 } 54 } 55 56 bool ipu7_isys_is_bayer_format(u32 code) 57 { 58 switch (ipu7_isys_mbus_code_to_mipi(code)) { 59 case MIPI_CSI2_DT_RAW8: 60 case MIPI_CSI2_DT_RAW10: 61 case MIPI_CSI2_DT_RAW12: 62 case MIPI_CSI2_DT_RAW14: 63 case MIPI_CSI2_DT_RAW16: 64 case MIPI_CSI2_DT_RAW20: 65 case MIPI_CSI2_DT_RAW24: 66 case MIPI_CSI2_DT_RAW28: 67 return true; 68 default: 69 return false; 70 } 71 } 72 73 u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y) 74 { 75 static const u32 code_map[] = { 76 MEDIA_BUS_FMT_SRGGB8_1X8, 77 MEDIA_BUS_FMT_SGRBG8_1X8, 78 MEDIA_BUS_FMT_SGBRG8_1X8, 79 MEDIA_BUS_FMT_SBGGR8_1X8, 80 MEDIA_BUS_FMT_SRGGB10_1X10, 81 MEDIA_BUS_FMT_SGRBG10_1X10, 82 MEDIA_BUS_FMT_SGBRG10_1X10, 83 MEDIA_BUS_FMT_SBGGR10_1X10, 84 MEDIA_BUS_FMT_SRGGB12_1X12, 85 MEDIA_BUS_FMT_SGRBG12_1X12, 86 MEDIA_BUS_FMT_SGBRG12_1X12, 87 MEDIA_BUS_FMT_SBGGR12_1X12, 88 }; 89 unsigned int i; 90 91 for (i = 0; i < ARRAY_SIZE(code_map); i++) 92 if (code_map[i] == code) 93 break; 94 95 if (WARN_ON(i == ARRAY_SIZE(code_map))) 96 return code; 97 98 return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))]; 99 } 100 101 int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, 102 struct v4l2_subdev_state *state, 103 struct v4l2_subdev_format *format) 104 { 105 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); 106 u32 code = asd->supported_codes[0]; 107 struct v4l2_mbus_framefmt *fmt; 108 u32 other_pad, other_stream; 109 struct v4l2_rect *crop; 110 unsigned int i; 111 int ret; 112 113 /* No transcoding, source and sink formats must match. */ 114 if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && 115 sd->entity.num_pads > 1) 116 return v4l2_subdev_get_fmt(sd, state, format); 117 118 format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH, 119 IPU_ISYS_MAX_WIDTH); 120 format->format.height = clamp(format->format.height, 121 IPU_ISYS_MIN_HEIGHT, 122 IPU_ISYS_MAX_HEIGHT); 123 124 for (i = 0; asd->supported_codes[i]; i++) { 125 if (asd->supported_codes[i] == format->format.code) { 126 code = asd->supported_codes[i]; 127 break; 128 } 129 } 130 format->format.code = code; 131 format->format.field = V4L2_FIELD_NONE; 132 133 /* Store the format and propagate it to the source pad. */ 134 fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); 135 if (!fmt) 136 return -EINVAL; 137 138 *fmt = format->format; 139 140 if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) 141 return 0; 142 143 /* propagate format to following source pad */ 144 fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 145 format->stream); 146 if (!fmt) 147 return -EINVAL; 148 149 *fmt = format->format; 150 151 ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 152 format->pad, 153 format->stream, 154 &other_pad, 155 &other_stream); 156 if (ret) 157 return -EINVAL; 158 159 crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); 160 /* reset crop */ 161 crop->left = 0; 162 crop->top = 0; 163 crop->width = fmt->width; 164 crop->height = fmt->height; 165 166 return 0; 167 } 168 169 int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, 170 struct v4l2_subdev_state *state, 171 struct v4l2_subdev_mbus_code_enum *code) 172 { 173 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); 174 const u32 *supported_codes = asd->supported_codes; 175 u32 index; 176 177 for (index = 0; supported_codes[index]; index++) { 178 if (index == code->index) { 179 code->code = supported_codes[index]; 180 return 0; 181 } 182 } 183 184 return -EINVAL; 185 } 186 187 static int subdev_set_routing(struct v4l2_subdev *sd, 188 struct v4l2_subdev_state *state, 189 struct v4l2_subdev_krouting *routing) 190 { 191 static const struct v4l2_mbus_framefmt fmt = { 192 .width = 4096, 193 .height = 3072, 194 .code = MEDIA_BUS_FMT_SGRBG10_1X10, 195 .field = V4L2_FIELD_NONE, 196 }; 197 int ret; 198 199 ret = v4l2_subdev_routing_validate(sd, routing, 200 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); 201 if (ret) 202 return ret; 203 204 return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); 205 } 206 207 int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, 208 struct v4l2_mbus_framefmt *format) 209 { 210 struct v4l2_subdev_state *state; 211 struct v4l2_mbus_framefmt *fmt; 212 213 if (!sd || !format) 214 return -EINVAL; 215 216 state = v4l2_subdev_lock_and_get_active_state(sd); 217 fmt = v4l2_subdev_state_get_format(state, pad, stream); 218 if (fmt) 219 *format = *fmt; 220 v4l2_subdev_unlock_state(state); 221 222 return fmt ? 0 : -EINVAL; 223 } 224 225 u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) 226 { 227 struct v4l2_subdev_state *state; 228 struct v4l2_subdev_route *routes; 229 u32 source_stream = 0; 230 unsigned int i; 231 232 state = v4l2_subdev_lock_and_get_active_state(sd); 233 if (!state) 234 return 0; 235 236 routes = state->routing.routes; 237 for (i = 0; i < state->routing.num_routes; i++) { 238 if (routes[i].source_pad == pad) { 239 source_stream = routes[i].source_stream; 240 break; 241 } 242 } 243 244 v4l2_subdev_unlock_state(state); 245 246 return source_stream; 247 } 248 249 static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, 250 struct v4l2_subdev_state *state) 251 { 252 struct v4l2_subdev_route route = { 253 .sink_pad = 0, 254 .sink_stream = 0, 255 .source_pad = 1, 256 .source_stream = 0, 257 .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 258 }; 259 struct v4l2_subdev_krouting routing = { 260 .num_routes = 1, 261 .routes = &route, 262 }; 263 264 return subdev_set_routing(sd, state, &routing); 265 } 266 267 int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, 268 struct v4l2_subdev_state *state, 269 enum v4l2_subdev_format_whence which, 270 struct v4l2_subdev_krouting *routing) 271 { 272 return subdev_set_routing(sd, state, routing); 273 } 274 275 static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { 276 .init_state = ipu7_isys_subdev_init_state, 277 }; 278 279 int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, 280 const struct v4l2_subdev_ops *ops, 281 unsigned int nr_ctrls, 282 unsigned int num_sink_pads, 283 unsigned int num_source_pads) 284 { 285 unsigned int num_pads = num_sink_pads + num_source_pads; 286 unsigned int i; 287 int ret; 288 289 v4l2_subdev_init(&asd->sd, ops); 290 291 asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 292 V4L2_SUBDEV_FL_HAS_EVENTS | 293 V4L2_SUBDEV_FL_STREAMS; 294 asd->sd.owner = THIS_MODULE; 295 asd->sd.dev = &asd->isys->adev->auxdev.dev; 296 asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 297 asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; 298 299 asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, 300 sizeof(*asd->pad), GFP_KERNEL); 301 if (!asd->pad) 302 return -ENOMEM; 303 304 for (i = 0; i < num_sink_pads; i++) 305 asd->pad[i].flags = MEDIA_PAD_FL_SINK | 306 MEDIA_PAD_FL_MUST_CONNECT; 307 308 for (i = num_sink_pads; i < num_pads; i++) 309 asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; 310 311 ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); 312 if (ret) { 313 pr_err("isys subdev init failed %d.\n", ret); 314 return ret; 315 } 316 317 if (asd->ctrl_init) { 318 ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); 319 if (ret) 320 goto out_media_entity_cleanup; 321 322 asd->ctrl_init(&asd->sd); 323 if (asd->ctrl_handler.error) { 324 ret = asd->ctrl_handler.error; 325 goto out_v4l2_ctrl_handler_free; 326 } 327 328 asd->sd.ctrl_handler = &asd->ctrl_handler; 329 } 330 331 asd->source = -1; 332 333 return 0; 334 335 out_v4l2_ctrl_handler_free: 336 v4l2_ctrl_handler_free(&asd->ctrl_handler); 337 338 out_media_entity_cleanup: 339 media_entity_cleanup(&asd->sd.entity); 340 341 return ret; 342 } 343 344 void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) 345 { 346 media_entity_cleanup(&asd->sd.entity); 347 v4l2_ctrl_handler_free(&asd->ctrl_handler); 348 } 349