FILE
rzv2h-ivc-subdev.c
File preview with syntax highlighting.
linux-6.19.14/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Renesas RZ/V2H(P) Input Video Control Block driver 4 * 5 * Copyright (C) 2025 Ideas on Board Oy 6 */ 7 8#include "rzv2h-ivc.h" 9 10#include <linux/media.h> 11#include <linux/media-bus-format.h> 12#include <linux/v4l2-mediabus.h> 13 14#include <media/v4l2-async.h> 15#include <media/v4l2-ctrls.h> 16#include <media/v4l2-dev.h> 17#include <media/v4l2-event.h> 18 19#define RZV2H_IVC_N_INPUTS_PER_OUTPUT 6 20 21/* 22 * We support 8/10/12/14/16/20 bit input in any bayer order, but the output 23 * format is fixed at 20-bits with the same order as the input. 24 */ 25static const struct { 26 u32 inputs[RZV2H_IVC_N_INPUTS_PER_OUTPUT]; 27 u32 output; 28} rzv2h_ivc_formats[] = { 29 { 30 .inputs = { 31 MEDIA_BUS_FMT_SBGGR8_1X8, 32 MEDIA_BUS_FMT_SBGGR10_1X10, 33 MEDIA_BUS_FMT_SBGGR12_1X12, 34 MEDIA_BUS_FMT_SBGGR14_1X14, 35 MEDIA_BUS_FMT_SBGGR16_1X16, 36 MEDIA_BUS_FMT_SBGGR20_1X20, 37 }, 38 .output = MEDIA_BUS_FMT_SBGGR20_1X20 39 }, 40 { 41 .inputs = { 42 MEDIA_BUS_FMT_SGBRG8_1X8, 43 MEDIA_BUS_FMT_SGBRG10_1X10, 44 MEDIA_BUS_FMT_SGBRG12_1X12, 45 MEDIA_BUS_FMT_SGBRG14_1X14, 46 MEDIA_BUS_FMT_SGBRG16_1X16, 47 MEDIA_BUS_FMT_SGBRG20_1X20, 48 }, 49 .output = MEDIA_BUS_FMT_SGBRG20_1X20 50 }, 51 { 52 .inputs = { 53 MEDIA_BUS_FMT_SGRBG8_1X8, 54 MEDIA_BUS_FMT_SGRBG10_1X10, 55 MEDIA_BUS_FMT_SGRBG12_1X12, 56 MEDIA_BUS_FMT_SGRBG14_1X14, 57 MEDIA_BUS_FMT_SGRBG16_1X16, 58 MEDIA_BUS_FMT_SGRBG20_1X20, 59 }, 60 .output = MEDIA_BUS_FMT_SGRBG20_1X20 61 }, 62 { 63 .inputs = { 64 MEDIA_BUS_FMT_SRGGB8_1X8, 65 MEDIA_BUS_FMT_SRGGB10_1X10, 66 MEDIA_BUS_FMT_SRGGB12_1X12, 67 MEDIA_BUS_FMT_SRGGB14_1X14, 68 MEDIA_BUS_FMT_SRGGB16_1X16, 69 MEDIA_BUS_FMT_SRGGB20_1X20, 70 }, 71 .output = MEDIA_BUS_FMT_SRGGB20_1X20 72 }, 73}; 74 75static u32 rzv2h_ivc_get_mbus_output_from_input(u32 mbus_code) 76{ 77 unsigned int i, j; 78 79 for (i = 0; i < ARRAY_SIZE(rzv2h_ivc_formats); i++) { 80 for (j = 0; j < RZV2H_IVC_N_INPUTS_PER_OUTPUT; j++) { 81 if (rzv2h_ivc_formats[i].inputs[j] == mbus_code) 82 return rzv2h_ivc_formats[i].output; 83 } 84 } 85 86 return 0; 87} 88 89static int rzv2h_ivc_enum_mbus_code(struct v4l2_subdev *sd, 90 struct v4l2_subdev_state *state, 91 struct v4l2_subdev_mbus_code_enum *code) 92{ 93 const struct v4l2_mbus_framefmt *fmt; 94 unsigned int order_index; 95 unsigned int index; 96 97 /* 98 * On the source pad, only the 20-bit format corresponding to the sink 99 * pad format's bayer order is supported. 100 */ 101 if (code->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) { 102 if (code->index) 103 return -EINVAL; 104 105 fmt = v4l2_subdev_state_get_format(state, 106 RZV2H_IVC_SUBDEV_SINK_PAD); 107 code->code = rzv2h_ivc_get_mbus_output_from_input(fmt->code); 108 109 return 0; 110 } 111 112 if (code->index >= ARRAY_SIZE(rzv2h_ivc_formats) * 113 RZV2H_IVC_N_INPUTS_PER_OUTPUT) 114 return -EINVAL; 115 116 order_index = code->index / RZV2H_IVC_N_INPUTS_PER_OUTPUT; 117 index = code->index % RZV2H_IVC_N_INPUTS_PER_OUTPUT; 118 119 code->code = rzv2h_ivc_formats[order_index].inputs[index]; 120 121 return 0; 122} 123 124static int rzv2h_ivc_enum_frame_size(struct v4l2_subdev *sd, 125 struct v4l2_subdev_state *state, 126 struct v4l2_subdev_frame_size_enum *fse) 127{ 128 const struct v4l2_mbus_framefmt *fmt; 129 130 if (fse->index > 0) 131 return -EINVAL; 132 133 if (fse->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) { 134 fmt = v4l2_subdev_state_get_format(state, 135 RZV2H_IVC_SUBDEV_SINK_PAD); 136 137 if (fse->code != rzv2h_ivc_get_mbus_output_from_input(fmt->code)) 138 return -EINVAL; 139 140 fse->min_width = fmt->width; 141 fse->max_width = fmt->width; 142 fse->min_height = fmt->height; 143 fse->max_height = fmt->height; 144 145 return 0; 146 } 147 148 if (!rzv2h_ivc_get_mbus_output_from_input(fse->code)) 149 return -EINVAL; 150 151 fse->min_width = RZV2H_IVC_MIN_WIDTH; 152 fse->max_width = RZV2H_IVC_MAX_WIDTH; 153 fse->min_height = RZV2H_IVC_MIN_HEIGHT; 154 fse->max_height = RZV2H_IVC_MAX_HEIGHT; 155 156 return 0; 157} 158 159static int rzv2h_ivc_set_fmt(struct v4l2_subdev *sd, 160 struct v4l2_subdev_state *state, 161 struct v4l2_subdev_format *format) 162{ 163 struct v4l2_mbus_framefmt *fmt = &format->format; 164 struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; 165 166 if (format->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) 167 return v4l2_subdev_get_fmt(sd, state, format); 168 169 sink_fmt = v4l2_subdev_state_get_format(state, 170 RZV2H_IVC_SUBDEV_SINK_PAD); 171 172 sink_fmt->code = rzv2h_ivc_get_mbus_output_from_input(fmt->code) ? 173 fmt->code : rzv2h_ivc_formats[0].inputs[0]; 174 175 sink_fmt->width = clamp(fmt->width, RZV2H_IVC_MIN_WIDTH, 176 RZV2H_IVC_MAX_WIDTH); 177 sink_fmt->height = clamp(fmt->height, RZV2H_IVC_MIN_HEIGHT, 178 RZV2H_IVC_MAX_HEIGHT); 179 180 *fmt = *sink_fmt; 181 182 src_fmt = v4l2_subdev_state_get_format(state, 183 RZV2H_IVC_SUBDEV_SOURCE_PAD); 184 *src_fmt = *sink_fmt; 185 src_fmt->code = rzv2h_ivc_get_mbus_output_from_input(sink_fmt->code); 186 187 return 0; 188} 189 190static int rzv2h_ivc_enable_streams(struct v4l2_subdev *sd, 191 struct v4l2_subdev_state *state, u32 pad, 192 u64 streams_mask) 193{ 194 /* 195 * We have a single source pad, which has a single stream. V4L2 core has 196 * already validated those things. The actual power-on and programming 197 * of registers will be done through the video device's .vidioc_streamon 198 * so there's nothing to actually do here... 199 */ 200 201 return 0; 202} 203 204static int rzv2h_ivc_disable_streams(struct v4l2_subdev *sd, 205 struct v4l2_subdev_state *state, u32 pad, 206 u64 streams_mask) 207{ 208 return 0; 209} 210 211static const struct v4l2_subdev_pad_ops rzv2h_ivc_pad_ops = { 212 .enum_mbus_code = rzv2h_ivc_enum_mbus_code, 213 .enum_frame_size = rzv2h_ivc_enum_frame_size, 214 .get_fmt = v4l2_subdev_get_fmt, 215 .set_fmt = rzv2h_ivc_set_fmt, 216 .enable_streams = rzv2h_ivc_enable_streams, 217 .disable_streams = rzv2h_ivc_disable_streams, 218}; 219 220static const struct v4l2_subdev_core_ops rzv2h_ivc_core_ops = { 221 .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 222 .unsubscribe_event = v4l2_event_subdev_unsubscribe, 223}; 224 225static const struct v4l2_subdev_ops rzv2h_ivc_subdev_ops = { 226 .core = &rzv2h_ivc_core_ops, 227 .pad = &rzv2h_ivc_pad_ops, 228}; 229 230static int rzv2h_ivc_init_state(struct v4l2_subdev *sd, 231 struct v4l2_subdev_state *state) 232{ 233 struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; 234 235 sink_fmt = v4l2_subdev_state_get_format(state, 236 RZV2H_IVC_SUBDEV_SINK_PAD); 237 sink_fmt->width = RZV2H_IVC_DEFAULT_WIDTH; 238 sink_fmt->height = RZV2H_IVC_DEFAULT_HEIGHT; 239 sink_fmt->field = V4L2_FIELD_NONE; 240 sink_fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; 241 sink_fmt->colorspace = V4L2_COLORSPACE_RAW; 242 sink_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace); 243 sink_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace); 244 sink_fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, 245 sink_fmt->colorspace, 246 sink_fmt->ycbcr_enc); 247 248 src_fmt = v4l2_subdev_state_get_format(state, 249 RZV2H_IVC_SUBDEV_SOURCE_PAD); 250 251 *src_fmt = *sink_fmt; 252 src_fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20; 253 254 return 0; 255} 256 257static int rzv2h_ivc_registered(struct v4l2_subdev *sd) 258{ 259 struct rzv2h_ivc *ivc = container_of(sd, struct rzv2h_ivc, subdev.sd); 260 261 return rzv2h_ivc_init_vdev(ivc, sd->v4l2_dev); 262} 263 264static const struct v4l2_subdev_internal_ops rzv2h_ivc_subdev_internal_ops = { 265 .init_state = rzv2h_ivc_init_state, 266 .registered = rzv2h_ivc_registered, 267}; 268 269static int rzv2h_ivc_link_validate(struct media_link *link) 270{ 271 struct video_device *vdev = 272 media_entity_to_video_device(link->source->entity); 273 struct rzv2h_ivc *ivc = video_get_drvdata(vdev); 274 struct v4l2_subdev *sd = 275 media_entity_to_v4l2_subdev(link->sink->entity); 276 const struct rzv2h_ivc_format *fmt; 277 const struct v4l2_pix_format_mplane *pix; 278 struct v4l2_subdev_state *state; 279 struct v4l2_mbus_framefmt *mf; 280 unsigned int i; 281 int ret = 0; 282 283 state = v4l2_subdev_lock_and_get_active_state(sd); 284 mf = v4l2_subdev_state_get_format(state, link->sink->index); 285 286 pix = &ivc->format.pix; 287 fmt = ivc->format.fmt; 288 289 if (mf->width != pix->width || mf->height != pix->height) { 290 dev_dbg(ivc->dev, 291 "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n", 292 link->source->entity->name, link->source->index, 293 link->sink->entity->name, link->sink->index, 294 mf->width, mf->height, pix->width, pix->height); 295 ret = -EPIPE; 296 } 297 298 for (i = 0; i < ARRAY_SIZE(fmt->mbus_codes); i++) 299 if (mf->code == fmt->mbus_codes[i]) 300 break; 301 302 if (i == ARRAY_SIZE(fmt->mbus_codes)) { 303 dev_dbg(ivc->dev, 304 "link '%s':%u -> '%s':%u not valid: pixel format %p4cc cannot produce mbus_code 0x%04x\n", 305 link->source->entity->name, link->source->index, 306 link->sink->entity->name, link->sink->index, 307 &pix->pixelformat, mf->code); 308 ret = -EPIPE; 309 } 310 311 v4l2_subdev_unlock_state(state); 312 313 return ret; 314} 315 316static const struct media_entity_operations rzv2h_ivc_media_ops = { 317 .link_validate = rzv2h_ivc_link_validate, 318}; 319 320int rzv2h_ivc_initialise_subdevice(struct rzv2h_ivc *ivc) 321{ 322 struct v4l2_subdev *sd; 323 int ret; 324 325 /* Initialise subdevice */ 326 sd = &ivc->subdev.sd; 327 sd->dev = ivc->dev; 328 v4l2_subdev_init(sd, &rzv2h_ivc_subdev_ops); 329 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 330 sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 331 sd->internal_ops = &rzv2h_ivc_subdev_internal_ops; 332 sd->entity.ops = &rzv2h_ivc_media_ops; 333 334 ivc->subdev.pads[RZV2H_IVC_SUBDEV_SINK_PAD].flags = MEDIA_PAD_FL_SINK; 335 ivc->subdev.pads[RZV2H_IVC_SUBDEV_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; 336 337 snprintf(sd->name, sizeof(sd->name), "rzv2h ivc block"); 338 339 ret = media_entity_pads_init(&sd->entity, RZV2H_IVC_NUM_SUBDEV_PADS, 340 ivc->subdev.pads); 341 if (ret) { 342 dev_err(ivc->dev, "failed to initialise media entity\n"); 343 return ret; 344 } 345 346 ret = v4l2_subdev_init_finalize(sd); 347 if (ret) { 348 dev_err(ivc->dev, "failed to finalize subdev init\n"); 349 goto err_cleanup_subdev_entity; 350 } 351 352 ret = v4l2_async_register_subdev(sd); 353 if (ret) { 354 dev_err(ivc->dev, "failed to register subdevice\n"); 355 goto err_cleanup_subdev; 356 } 357 358 return 0; 359 360err_cleanup_subdev: 361 v4l2_subdev_cleanup(sd); 362err_cleanup_subdev_entity: 363 media_entity_cleanup(&sd->entity); 364 365 return ret; 366} 367 368void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc) 369{ 370 struct v4l2_subdev *sd = &ivc->subdev.sd; 371 372 v4l2_subdev_cleanup(sd); 373 media_entity_remove_links(&sd->entity); 374 v4l2_async_unregister_subdev(sd); 375 media_entity_cleanup(&sd->entity); 376}
~/
Read-only file preview.