FILE
ipu7-isys-subdev.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/ipu7/ipu7-isys-subdev.c
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 22unsigned 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 56bool 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 73u32 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 101int 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 169int 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 187static 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 struct v4l2_subdev_route *route; 198 int ret; 199 200 ret = v4l2_subdev_routing_validate(sd, routing, 201 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | 202 V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); 203 if (ret) 204 return ret; 205 206 /* 207 * The device doesn't support source multiplexing, set all source 208 * streams to 0 to simplify stream handling through the driver. 209 */ 210 for_each_active_route(routing, route) 211 route->source_stream = 0; 212 213 return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); 214} 215 216int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, 217 struct v4l2_mbus_framefmt *format) 218{ 219 struct v4l2_subdev_state *state; 220 struct v4l2_mbus_framefmt *fmt; 221 222 if (!sd || !format) 223 return -EINVAL; 224 225 state = v4l2_subdev_lock_and_get_active_state(sd); 226 fmt = v4l2_subdev_state_get_format(state, pad, stream); 227 if (fmt) 228 *format = *fmt; 229 v4l2_subdev_unlock_state(state); 230 231 return fmt ? 0 : -EINVAL; 232} 233 234static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, 235 struct v4l2_subdev_state *state) 236{ 237 struct v4l2_subdev_route route = { 238 .sink_pad = 0, 239 .sink_stream = 0, 240 .source_pad = 1, 241 .source_stream = 0, 242 .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 243 }; 244 struct v4l2_subdev_krouting routing = { 245 .num_routes = 1, 246 .routes = &route, 247 }; 248 249 return subdev_set_routing(sd, state, &routing); 250} 251 252int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, 253 struct v4l2_subdev_state *state, 254 enum v4l2_subdev_format_whence which, 255 struct v4l2_subdev_krouting *routing) 256{ 257 return subdev_set_routing(sd, state, routing); 258} 259 260static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { 261 .init_state = ipu7_isys_subdev_init_state, 262}; 263 264int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, 265 const struct v4l2_subdev_ops *ops, 266 unsigned int nr_ctrls, 267 unsigned int num_sink_pads, 268 unsigned int num_source_pads) 269{ 270 unsigned int num_pads = num_sink_pads + num_source_pads; 271 unsigned int i; 272 int ret; 273 274 v4l2_subdev_init(&asd->sd, ops); 275 276 asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 277 V4L2_SUBDEV_FL_HAS_EVENTS | 278 V4L2_SUBDEV_FL_STREAMS; 279 asd->sd.owner = THIS_MODULE; 280 asd->sd.dev = &asd->isys->adev->auxdev.dev; 281 asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 282 asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; 283 284 asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, 285 sizeof(*asd->pad), GFP_KERNEL); 286 if (!asd->pad) 287 return -ENOMEM; 288 289 for (i = 0; i < num_sink_pads; i++) 290 asd->pad[i].flags = MEDIA_PAD_FL_SINK | 291 MEDIA_PAD_FL_MUST_CONNECT; 292 293 for (i = num_sink_pads; i < num_pads; i++) 294 asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; 295 296 ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); 297 if (ret) { 298 pr_err("isys subdev init failed %d.\n", ret); 299 return ret; 300 } 301 302 if (asd->ctrl_init) { 303 ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); 304 if (ret) 305 goto out_media_entity_cleanup; 306 307 asd->ctrl_init(&asd->sd); 308 if (asd->ctrl_handler.error) { 309 ret = asd->ctrl_handler.error; 310 goto out_v4l2_ctrl_handler_free; 311 } 312 313 asd->sd.ctrl_handler = &asd->ctrl_handler; 314 } 315 316 asd->source = -1; 317 318 return 0; 319 320out_v4l2_ctrl_handler_free: 321 v4l2_ctrl_handler_free(&asd->ctrl_handler); 322 323out_media_entity_cleanup: 324 media_entity_cleanup(&asd->sd.entity); 325 326 return ret; 327} 328 329void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) 330{ 331 media_entity_cleanup(&asd->sd.entity); 332 v4l2_ctrl_handler_free(&asd->ctrl_handler); 333}
~/
Read-only file preview.