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