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_Y16_1X16: 29 case MEDIA_BUS_FMT_META_16: 30 return 16; 31 case MEDIA_BUS_FMT_SBGGR12_1X12: 32 case MEDIA_BUS_FMT_SGBRG12_1X12: 33 case MEDIA_BUS_FMT_SGRBG12_1X12: 34 case MEDIA_BUS_FMT_SRGGB12_1X12: 35 case MEDIA_BUS_FMT_Y12_1X12: 36 case MEDIA_BUS_FMT_META_12: 37 return 12; 38 case MEDIA_BUS_FMT_SBGGR10_1X10: 39 case MEDIA_BUS_FMT_SGBRG10_1X10: 40 case MEDIA_BUS_FMT_SGRBG10_1X10: 41 case MEDIA_BUS_FMT_SRGGB10_1X10: 42 case MEDIA_BUS_FMT_Y10_1X10: 43 case MEDIA_BUS_FMT_META_10: 44 return 10; 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 case MEDIA_BUS_FMT_Y8_1X8: 50 case MEDIA_BUS_FMT_META_8: 51 return 8; 52 default: 53 WARN_ON(1); 54 return 8; 55 } 56 } 57 58 unsigned int ipu6_isys_mbus_code_to_mipi(u32 code) 59 { 60 switch (code) { 61 case MEDIA_BUS_FMT_RGB565_1X16: 62 return MIPI_CSI2_DT_RGB565; 63 case MEDIA_BUS_FMT_RGB888_1X24: 64 return MIPI_CSI2_DT_RGB888; 65 case MEDIA_BUS_FMT_UYVY8_1X16: 66 case MEDIA_BUS_FMT_YUYV8_1X16: 67 return MIPI_CSI2_DT_YUV422_8B; 68 case MEDIA_BUS_FMT_SBGGR16_1X16: 69 case MEDIA_BUS_FMT_SGBRG16_1X16: 70 case MEDIA_BUS_FMT_SGRBG16_1X16: 71 case MEDIA_BUS_FMT_SRGGB16_1X16: 72 case MEDIA_BUS_FMT_Y16_1X16: 73 return MIPI_CSI2_DT_RAW16; 74 case MEDIA_BUS_FMT_SBGGR12_1X12: 75 case MEDIA_BUS_FMT_SGBRG12_1X12: 76 case MEDIA_BUS_FMT_SGRBG12_1X12: 77 case MEDIA_BUS_FMT_SRGGB12_1X12: 78 case MEDIA_BUS_FMT_Y12_1X12: 79 return MIPI_CSI2_DT_RAW12; 80 case MEDIA_BUS_FMT_SBGGR10_1X10: 81 case MEDIA_BUS_FMT_SGBRG10_1X10: 82 case MEDIA_BUS_FMT_SGRBG10_1X10: 83 case MEDIA_BUS_FMT_SRGGB10_1X10: 84 case MEDIA_BUS_FMT_Y10_1X10: 85 return MIPI_CSI2_DT_RAW10; 86 case MEDIA_BUS_FMT_SBGGR8_1X8: 87 case MEDIA_BUS_FMT_SGBRG8_1X8: 88 case MEDIA_BUS_FMT_SGRBG8_1X8: 89 case MEDIA_BUS_FMT_SRGGB8_1X8: 90 case MEDIA_BUS_FMT_Y8_1X8: 91 return MIPI_CSI2_DT_RAW8; 92 case MEDIA_BUS_FMT_META_8: 93 case MEDIA_BUS_FMT_META_10: 94 case MEDIA_BUS_FMT_META_12: 95 case MEDIA_BUS_FMT_META_16: 96 case MEDIA_BUS_FMT_META_24: 97 return MIPI_CSI2_DT_EMBEDDED_8B; 98 default: 99 /* return unavailable MIPI data type - 0x3f */ 100 WARN_ON(1); 101 return 0x3f; 102 } 103 } 104 105 bool ipu6_isys_is_bayer_format(u32 code) 106 { 107 switch (code) { 108 case MEDIA_BUS_FMT_SBGGR8_1X8: 109 case MEDIA_BUS_FMT_SGBRG8_1X8: 110 case MEDIA_BUS_FMT_SGRBG8_1X8: 111 case MEDIA_BUS_FMT_SRGGB8_1X8: 112 case MEDIA_BUS_FMT_SBGGR10_1X10: 113 case MEDIA_BUS_FMT_SGBRG10_1X10: 114 case MEDIA_BUS_FMT_SGRBG10_1X10: 115 case MEDIA_BUS_FMT_SRGGB10_1X10: 116 case MEDIA_BUS_FMT_SBGGR12_1X12: 117 case MEDIA_BUS_FMT_SGBRG12_1X12: 118 case MEDIA_BUS_FMT_SGRBG12_1X12: 119 case MEDIA_BUS_FMT_SRGGB12_1X12: 120 case MEDIA_BUS_FMT_SRGGB16_1X16: 121 case MEDIA_BUS_FMT_SGRBG16_1X16: 122 case MEDIA_BUS_FMT_SGBRG16_1X16: 123 case MEDIA_BUS_FMT_SBGGR16_1X16: 124 return true; 125 default: 126 return false; 127 } 128 } 129 130 u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y) 131 { 132 static const u32 code_map[] = { 133 MEDIA_BUS_FMT_SRGGB8_1X8, 134 MEDIA_BUS_FMT_SGRBG8_1X8, 135 MEDIA_BUS_FMT_SGBRG8_1X8, 136 MEDIA_BUS_FMT_SBGGR8_1X8, 137 MEDIA_BUS_FMT_SRGGB10_1X10, 138 MEDIA_BUS_FMT_SGRBG10_1X10, 139 MEDIA_BUS_FMT_SGBRG10_1X10, 140 MEDIA_BUS_FMT_SBGGR10_1X10, 141 MEDIA_BUS_FMT_SRGGB12_1X12, 142 MEDIA_BUS_FMT_SGRBG12_1X12, 143 MEDIA_BUS_FMT_SGBRG12_1X12, 144 MEDIA_BUS_FMT_SBGGR12_1X12, 145 MEDIA_BUS_FMT_SRGGB16_1X16, 146 MEDIA_BUS_FMT_SGRBG16_1X16, 147 MEDIA_BUS_FMT_SGBRG16_1X16, 148 MEDIA_BUS_FMT_SBGGR16_1X16, 149 }; 150 u32 i; 151 152 for (i = 0; i < ARRAY_SIZE(code_map); i++) 153 if (code_map[i] == code) 154 break; 155 156 if (WARN_ON(i == ARRAY_SIZE(code_map))) 157 return code; 158 159 return code_map[i ^ (((y & 1) << 1) | (x & 1))]; 160 } 161 162 int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, 163 struct v4l2_subdev_state *state, 164 struct v4l2_subdev_format *format) 165 { 166 struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); 167 struct v4l2_mbus_framefmt *fmt; 168 struct v4l2_rect *crop; 169 u32 code = asd->supported_codes[0]; 170 u32 other_pad, other_stream; 171 unsigned int i; 172 int ret; 173 174 /* No transcoding, source and sink formats must match. */ 175 if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && 176 sd->entity.num_pads > 1) 177 return v4l2_subdev_get_fmt(sd, state, format); 178 179 format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH, 180 IPU6_ISYS_MAX_WIDTH); 181 format->format.height = clamp(format->format.height, 182 IPU6_ISYS_MIN_HEIGHT, 183 IPU6_ISYS_MAX_HEIGHT); 184 185 for (i = 0; asd->supported_codes[i]; i++) { 186 if (asd->supported_codes[i] == format->format.code) { 187 code = asd->supported_codes[i]; 188 break; 189 } 190 } 191 format->format.code = code; 192 format->format.field = V4L2_FIELD_NONE; 193 194 /* Store the format and propagate it to the source pad. */ 195 fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); 196 if (!fmt) 197 return -EINVAL; 198 199 *fmt = format->format; 200 201 if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) 202 return 0; 203 204 /* propagate format to following source pad */ 205 fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 206 format->stream); 207 if (!fmt) 208 return -EINVAL; 209 210 *fmt = format->format; 211 212 ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 213 format->pad, 214 format->stream, 215 &other_pad, 216 &other_stream); 217 if (ret) 218 return -EINVAL; 219 220 crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); 221 /* reset crop */ 222 crop->left = 0; 223 crop->top = 0; 224 crop->width = fmt->width; 225 crop->height = fmt->height; 226 227 return 0; 228 } 229 230 int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, 231 struct v4l2_subdev_state *state, 232 struct v4l2_subdev_mbus_code_enum *code) 233 { 234 struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); 235 const u32 *supported_codes = asd->supported_codes; 236 u32 index; 237 238 for (index = 0; supported_codes[index]; index++) { 239 if (index == code->index) { 240 code->code = supported_codes[index]; 241 return 0; 242 } 243 } 244 245 return -EINVAL; 246 } 247 248 static int subdev_set_routing(struct v4l2_subdev *sd, 249 struct v4l2_subdev_state *state, 250 struct v4l2_subdev_krouting *routing) 251 { 252 static const struct v4l2_mbus_framefmt format = { 253 .width = 4096, 254 .height = 3072, 255 .code = MEDIA_BUS_FMT_SGRBG10_1X10, 256 .field = V4L2_FIELD_NONE, 257 }; 258 int ret; 259 260 ret = v4l2_subdev_routing_validate(sd, routing, 261 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); 262 if (ret) 263 return ret; 264 265 return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); 266 } 267 268 int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, 269 struct v4l2_mbus_framefmt *format) 270 { 271 struct v4l2_mbus_framefmt *fmt; 272 struct v4l2_subdev_state *state; 273 274 if (!sd || !format) 275 return -EINVAL; 276 277 state = v4l2_subdev_lock_and_get_active_state(sd); 278 fmt = v4l2_subdev_state_get_format(state, pad, stream); 279 if (fmt) 280 *format = *fmt; 281 v4l2_subdev_unlock_state(state); 282 283 return fmt ? 0 : -EINVAL; 284 } 285 286 int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, 287 struct v4l2_rect *crop) 288 { 289 struct v4l2_subdev_state *state; 290 struct v4l2_rect *rect; 291 292 if (!sd || !crop) 293 return -EINVAL; 294 295 state = v4l2_subdev_lock_and_get_active_state(sd); 296 rect = v4l2_subdev_state_get_crop(state, pad, stream); 297 if (rect) 298 *crop = *rect; 299 v4l2_subdev_unlock_state(state); 300 301 return rect ? 0 : -EINVAL; 302 } 303 304 u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) 305 { 306 struct v4l2_subdev_state *state; 307 struct v4l2_subdev_route *routes; 308 unsigned int i; 309 u32 source_stream = 0; 310 311 state = v4l2_subdev_lock_and_get_active_state(sd); 312 if (!state) 313 return 0; 314 315 routes = state->routing.routes; 316 for (i = 0; i < state->routing.num_routes; i++) { 317 if (routes[i].source_pad == pad) { 318 source_stream = routes[i].source_stream; 319 break; 320 } 321 } 322 323 v4l2_subdev_unlock_state(state); 324 325 return source_stream; 326 } 327 328 static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd, 329 struct v4l2_subdev_state *state) 330 { 331 struct v4l2_subdev_route route = { 332 .sink_pad = 0, 333 .sink_stream = 0, 334 .source_pad = 1, 335 .source_stream = 0, 336 .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 337 }; 338 struct v4l2_subdev_krouting routing = { 339 .num_routes = 1, 340 .routes = &route, 341 }; 342 343 return subdev_set_routing(sd, state, &routing); 344 } 345 346 int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, 347 struct v4l2_subdev_state *state, 348 enum v4l2_subdev_format_whence which, 349 struct v4l2_subdev_krouting *routing) 350 { 351 return subdev_set_routing(sd, state, routing); 352 } 353 354 static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = { 355 .init_state = ipu6_isys_subdev_init_state, 356 }; 357 358 int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, 359 const struct v4l2_subdev_ops *ops, 360 unsigned int nr_ctrls, 361 unsigned int num_sink_pads, 362 unsigned int num_source_pads) 363 { 364 unsigned int num_pads = num_sink_pads + num_source_pads; 365 unsigned int i; 366 int ret; 367 368 v4l2_subdev_init(&asd->sd, ops); 369 370 asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 371 V4L2_SUBDEV_FL_HAS_EVENTS | 372 V4L2_SUBDEV_FL_STREAMS; 373 asd->sd.owner = THIS_MODULE; 374 asd->sd.dev = &asd->isys->adev->auxdev.dev; 375 asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 376 asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops; 377 378 asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, 379 sizeof(*asd->pad), GFP_KERNEL); 380 if (!asd->pad) 381 return -ENOMEM; 382 383 for (i = 0; i < num_sink_pads; i++) 384 asd->pad[i].flags = MEDIA_PAD_FL_SINK | 385 MEDIA_PAD_FL_MUST_CONNECT; 386 387 for (i = num_sink_pads; i < num_pads; i++) 388 asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; 389 390 ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); 391 if (ret) 392 return ret; 393 394 if (asd->ctrl_init) { 395 ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); 396 if (ret) 397 goto out_media_entity_cleanup; 398 399 asd->ctrl_init(&asd->sd); 400 if (asd->ctrl_handler.error) { 401 ret = asd->ctrl_handler.error; 402 goto out_v4l2_ctrl_handler_free; 403 } 404 405 asd->sd.ctrl_handler = &asd->ctrl_handler; 406 } 407 408 asd->source = -1; 409 410 return 0; 411 412 out_v4l2_ctrl_handler_free: 413 v4l2_ctrl_handler_free(&asd->ctrl_handler); 414 415 out_media_entity_cleanup: 416 media_entity_cleanup(&asd->sd.entity); 417 418 return ret; 419 } 420 421 void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd) 422 { 423 media_entity_cleanup(&asd->sd.entity); 424 v4l2_ctrl_handler_free(&asd->ctrl_handler); 425 } 426