1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2013--2024 Intel Corporation 4 */ 5 6 #include <linux/bug.h> 7 #include <linux/device.h> 8 #include <linux/minmax.h> 9 10 #include <media/media-entity.h> 11 #include <media/mipi-csi2.h> 12 #include <media/v4l2-ctrls.h> 13 #include <media/v4l2-subdev.h> 14 15 #include "ipu6-bus.h" 16 #include "ipu6-isys.h" 17 #include "ipu6-isys-subdev.h" 18 19 unsigned int ipu6_isys_mbus_code_to_bpp(u32 code) 20 { 21 switch (code) { 22 case MEDIA_BUS_FMT_RGB888_1X24: 23 case MEDIA_BUS_FMT_META_24: 24 return 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: 29 return 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: 35 return 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: 41 return 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: 47 return 8; 48 default: 49 WARN_ON(1); 50 return 8; 51 } 52 } 53 54 unsigned int ipu6_isys_mbus_code_to_mipi(u32 code) 55 { 56 switch (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; 84 default: 85 /* return unavailable MIPI data type - 0x3f */ 86 WARN_ON(1); 87 return 0x3f; 88 } 89 } 90 91 bool ipu6_isys_is_bayer_format(u32 code) 92 { 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: 102 return true; 103 default: 104 return false; 105 } 106 } 107 108 u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y) 109 { 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, 127 }; 128 u32 i; 129 130 for (i = 0; i < ARRAY_SIZE(code_map); i++) 131 if (code_map[i] == code) 132 break; 133 134 if (WARN_ON(i == ARRAY_SIZE(code_map))) 135 return code; 136 137 return code_map[i ^ (((y & 1) << 1) | (x & 1))]; 138 } 139 140 int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, 141 struct v4l2_subdev_state *state, 142 struct v4l2_subdev_format *format) 143 { 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; 149 unsigned int i; 150 int ret; 151 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); 156 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); 162 163 for (i = 0; asd->supported_codes[i]; i++) { 164 if (asd->supported_codes[i] == format->format.code) { 165 code = asd->supported_codes[i]; 166 break; 167 } 168 } 169 format->format.code = code; 170 format->format.field = V4L2_FIELD_NONE; 171 172 /* Store the format and propagate it to the source pad. */ 173 fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); 174 if (!fmt) 175 return -EINVAL; 176 177 *fmt = format->format; 178 179 if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) 180 return 0; 181 182 /* propagate format to following source pad */ 183 fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 184 format->stream); 185 if (!fmt) 186 return -EINVAL; 187 188 *fmt = format->format; 189 190 ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 191 format->pad, 192 format->stream, 193 &other_pad, 194 &other_stream); 195 if (ret) 196 return -EINVAL; 197 198 crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); 199 /* reset crop */ 200 crop->left = 0; 201 crop->top = 0; 202 crop->width = fmt->width; 203 crop->height = fmt->height; 204 205 return 0; 206 } 207 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) 211 { 212 struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); 213 const u32 *supported_codes = asd->supported_codes; 214 u32 index; 215 216 for (index = 0; supported_codes[index]; index++) { 217 if (index == code->index) { 218 code->code = supported_codes[index]; 219 return 0; 220 } 221 } 222 223 return -EINVAL; 224 } 225 226 static int subdev_set_routing(struct v4l2_subdev *sd, 227 struct v4l2_subdev_state *state, 228 struct v4l2_subdev_krouting *routing) 229 { 230 static const struct v4l2_mbus_framefmt format = { 231 .width = 4096, 232 .height = 3072, 233 .code = MEDIA_BUS_FMT_SGRBG10_1X10, 234 .field = V4L2_FIELD_NONE, 235 }; 236 int ret; 237 238 ret = v4l2_subdev_routing_validate(sd, routing, 239 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); 240 if (ret) 241 return ret; 242 243 return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); 244 } 245 246 int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, 247 struct v4l2_mbus_framefmt *format) 248 { 249 struct v4l2_mbus_framefmt *fmt; 250 struct v4l2_subdev_state *state; 251 252 if (!sd || !format) 253 return -EINVAL; 254 255 state = v4l2_subdev_lock_and_get_active_state(sd); 256 fmt = v4l2_subdev_state_get_format(state, pad, stream); 257 if (fmt) 258 *format = *fmt; 259 v4l2_subdev_unlock_state(state); 260 261 return fmt ? 0 : -EINVAL; 262 } 263 264 int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, 265 struct v4l2_rect *crop) 266 { 267 struct v4l2_subdev_state *state; 268 struct v4l2_rect *rect; 269 270 if (!sd || !crop) 271 return -EINVAL; 272 273 state = v4l2_subdev_lock_and_get_active_state(sd); 274 rect = v4l2_subdev_state_get_crop(state, pad, stream); 275 if (rect) 276 *crop = *rect; 277 v4l2_subdev_unlock_state(state); 278 279 return rect ? 0 : -EINVAL; 280 } 281 282 u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) 283 { 284 struct v4l2_subdev_state *state; 285 struct v4l2_subdev_route *routes; 286 unsigned int i; 287 u32 source_stream = 0; 288 289 state = v4l2_subdev_lock_and_get_active_state(sd); 290 if (!state) 291 return 0; 292 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; 297 break; 298 } 299 } 300 301 v4l2_subdev_unlock_state(state); 302 303 return source_stream; 304 } 305 306 static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd, 307 struct v4l2_subdev_state *state) 308 { 309 struct v4l2_subdev_route route = { 310 .sink_pad = 0, 311 .sink_stream = 0, 312 .source_pad = 1, 313 .source_stream = 0, 314 .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 315 }; 316 struct v4l2_subdev_krouting routing = { 317 .num_routes = 1, 318 .routes = &route, 319 }; 320 321 return subdev_set_routing(sd, state, &routing); 322 } 323 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) 328 { 329 return subdev_set_routing(sd, state, routing); 330 } 331 332 static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = { 333 .init_state = ipu6_isys_subdev_init_state, 334 }; 335 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) 341 { 342 unsigned int num_pads = num_sink_pads + num_source_pads; 343 unsigned int i; 344 int ret; 345 346 v4l2_subdev_init(&asd->sd, ops); 347 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; 355 356 asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, 357 sizeof(*asd->pad), GFP_KERNEL); 358 if (!asd->pad) 359 return -ENOMEM; 360 361 for (i = 0; i < num_sink_pads; i++) 362 asd->pad[i].flags = MEDIA_PAD_FL_SINK | 363 MEDIA_PAD_FL_MUST_CONNECT; 364 365 for (i = num_sink_pads; i < num_pads; i++) 366 asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; 367 368 ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); 369 if (ret) 370 return ret; 371 372 if (asd->ctrl_init) { 373 ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); 374 if (ret) 375 goto out_media_entity_cleanup; 376 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; 381 } 382 383 asd->sd.ctrl_handler = &asd->ctrl_handler; 384 } 385 386 asd->source = -1; 387 388 return 0; 389 390 out_v4l2_ctrl_handler_free: 391 v4l2_ctrl_handler_free(&asd->ctrl_handler); 392 393 out_media_entity_cleanup: 394 media_entity_cleanup(&asd->sd.entity); 395 396 return ret; 397 } 398 399 void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd) 400 { 401 media_entity_cleanup(&asd->sd.entity); 402 v4l2_ctrl_handler_free(&asd->ctrl_handler); 403 } 404