1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for Renesas RZ/G2L CRU 4 * 5 * Copyright (C) 2022 Renesas Electronics Corp. 6 */ 7 8 #include <linux/delay.h> 9 #include <media/mipi-csi2.h> 10 11 #include "rzg2l-cru.h" 12 #include "rzg2l-cru-regs.h" 13 14 static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { 15 { 16 .codes = { 17 MEDIA_BUS_FMT_UYVY8_1X16, 18 }, 19 .datatype = MIPI_CSI2_DT_YUV422_8B, 20 .format = V4L2_PIX_FMT_UYVY, 21 .icndmr = ICnDMR_YCMODE_UYVY, 22 .yuv = true, 23 }, 24 { 25 .codes = { 26 MEDIA_BUS_FMT_SBGGR8_1X8, 27 }, 28 .format = V4L2_PIX_FMT_SBGGR8, 29 .datatype = MIPI_CSI2_DT_RAW8, 30 .icndmr = 0, 31 .yuv = false, 32 }, 33 { 34 .codes = { 35 MEDIA_BUS_FMT_SGBRG8_1X8, 36 }, 37 .format = V4L2_PIX_FMT_SGBRG8, 38 .datatype = MIPI_CSI2_DT_RAW8, 39 .icndmr = 0, 40 .yuv = false, 41 }, 42 { 43 .codes = { 44 MEDIA_BUS_FMT_SGRBG8_1X8, 45 }, 46 .format = V4L2_PIX_FMT_SGRBG8, 47 .datatype = MIPI_CSI2_DT_RAW8, 48 .icndmr = 0, 49 .yuv = false, 50 }, 51 { 52 .codes = { 53 MEDIA_BUS_FMT_SRGGB8_1X8, 54 }, 55 .format = V4L2_PIX_FMT_SRGGB8, 56 .datatype = MIPI_CSI2_DT_RAW8, 57 .icndmr = 0, 58 .yuv = false, 59 }, 60 { 61 .codes = { 62 MEDIA_BUS_FMT_SBGGR10_1X10, 63 MEDIA_BUS_FMT_SGBRG10_1X10, 64 MEDIA_BUS_FMT_SGRBG10_1X10, 65 MEDIA_BUS_FMT_SRGGB10_1X10 66 }, 67 .format = V4L2_PIX_FMT_RAW_CRU10, 68 .datatype = MIPI_CSI2_DT_RAW10, 69 .icndmr = 0, 70 .yuv = false, 71 }, 72 { 73 .codes = { 74 MEDIA_BUS_FMT_SBGGR12_1X12, 75 MEDIA_BUS_FMT_SGBRG12_1X12, 76 MEDIA_BUS_FMT_SGRBG12_1X12, 77 MEDIA_BUS_FMT_SRGGB12_1X12 78 }, 79 .format = V4L2_PIX_FMT_RAW_CRU12, 80 .datatype = MIPI_CSI2_DT_RAW12, 81 .icndmr = 0, 82 .yuv = false, 83 }, 84 { 85 .codes = { 86 MEDIA_BUS_FMT_SBGGR14_1X14, 87 MEDIA_BUS_FMT_SGBRG14_1X14, 88 MEDIA_BUS_FMT_SGRBG14_1X14, 89 MEDIA_BUS_FMT_SRGGB14_1X14 90 }, 91 .format = V4L2_PIX_FMT_RAW_CRU14, 92 .datatype = MIPI_CSI2_DT_RAW14, 93 .icndmr = 0, 94 .yuv = false, 95 }, 96 }; 97 98 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) 99 { 100 unsigned int i, j; 101 102 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { 103 for (j = 0; j < ARRAY_SIZE(rzg2l_cru_ip_formats[i].codes); j++) { 104 if (rzg2l_cru_ip_formats[i].codes[j] == code) 105 return &rzg2l_cru_ip_formats[i]; 106 } 107 } 108 109 return NULL; 110 } 111 112 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format) 113 { 114 unsigned int i; 115 116 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { 117 if (rzg2l_cru_ip_formats[i].format == format) 118 return &rzg2l_cru_ip_formats[i]; 119 } 120 121 return NULL; 122 } 123 124 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index) 125 { 126 if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) 127 return NULL; 128 129 return &rzg2l_cru_ip_formats[index]; 130 } 131 132 bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt, 133 unsigned int code) 134 { 135 unsigned int i; 136 137 for (i = 0; i < ARRAY_SIZE(fmt->codes); i++) 138 if (fmt->codes[i] == code) 139 return true; 140 141 return false; 142 } 143 struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) 144 { 145 struct v4l2_subdev_state *state; 146 struct v4l2_mbus_framefmt *fmt; 147 148 state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); 149 fmt = v4l2_subdev_state_get_format(state, 1); 150 v4l2_subdev_unlock_state(state); 151 152 return fmt; 153 } 154 155 static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable) 156 { 157 struct rzg2l_cru_dev *cru; 158 int s_stream_ret = 0; 159 int ret; 160 161 cru = v4l2_get_subdevdata(sd); 162 163 if (!enable) { 164 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 165 if (ret) 166 s_stream_ret = ret; 167 168 ret = v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 169 if (ret == -ENOIOCTLCMD) 170 ret = 0; 171 if (ret && !s_stream_ret) 172 s_stream_ret = ret; 173 rzg2l_cru_stop_image_processing(cru); 174 } else { 175 ret = v4l2_subdev_call(cru->ip.remote, video, pre_streamon, 0); 176 if (ret == -ENOIOCTLCMD) 177 ret = 0; 178 if (ret) 179 return ret; 180 181 fsleep(1000); 182 183 ret = rzg2l_cru_start_image_processing(cru); 184 if (ret) { 185 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 186 return ret; 187 } 188 189 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 190 if (!ret || ret == -ENOIOCTLCMD) 191 return 0; 192 193 s_stream_ret = ret; 194 195 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 196 rzg2l_cru_stop_image_processing(cru); 197 } 198 199 return s_stream_ret; 200 } 201 202 static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, 203 struct v4l2_subdev_state *state, 204 struct v4l2_subdev_format *fmt) 205 { 206 struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); 207 const struct rzg2l_cru_info *info = cru->info; 208 struct v4l2_mbus_framefmt *src_format; 209 struct v4l2_mbus_framefmt *sink_format; 210 211 src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); 212 if (fmt->pad == RZG2L_CRU_IP_SOURCE) { 213 fmt->format = *src_format; 214 return 0; 215 } 216 217 sink_format = v4l2_subdev_state_get_format(state, fmt->pad); 218 219 if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) 220 sink_format->code = rzg2l_cru_ip_formats[0].codes[0]; 221 else 222 sink_format->code = fmt->format.code; 223 224 sink_format->field = V4L2_FIELD_NONE; 225 sink_format->colorspace = fmt->format.colorspace; 226 sink_format->xfer_func = fmt->format.xfer_func; 227 sink_format->ycbcr_enc = fmt->format.ycbcr_enc; 228 sink_format->quantization = fmt->format.quantization; 229 sink_format->width = clamp_t(u32, fmt->format.width, 230 RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width); 231 sink_format->height = clamp_t(u32, fmt->format.height, 232 RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height); 233 234 fmt->format = *sink_format; 235 236 /* propagate format to source pad */ 237 *src_format = *sink_format; 238 239 return 0; 240 } 241 242 static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd, 243 struct v4l2_subdev_state *state, 244 struct v4l2_subdev_mbus_code_enum *code) 245 { 246 unsigned int index = code->index; 247 unsigned int i, j; 248 249 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { 250 const struct rzg2l_cru_ip_format *fmt = &rzg2l_cru_ip_formats[i]; 251 252 for (j = 0; j < ARRAY_SIZE(fmt->codes); j++) { 253 if (!fmt->codes[j]) 254 continue; 255 256 if (!index) { 257 code->code = fmt->codes[j]; 258 return 0; 259 } 260 261 index--; 262 } 263 } 264 265 return -EINVAL; 266 } 267 268 static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, 269 struct v4l2_subdev_state *state, 270 struct v4l2_subdev_frame_size_enum *fse) 271 { 272 struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); 273 const struct rzg2l_cru_info *info = cru->info; 274 275 if (fse->index != 0) 276 return -EINVAL; 277 278 if (!rzg2l_cru_ip_code_to_fmt(fse->code)) 279 return -EINVAL; 280 281 fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; 282 fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; 283 fse->max_width = info->max_width; 284 fse->max_height = info->max_height; 285 286 return 0; 287 } 288 289 static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, 290 struct v4l2_subdev_state *sd_state) 291 { 292 struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; 293 294 fmt.format.width = RZG2L_CRU_MIN_INPUT_WIDTH; 295 fmt.format.height = RZG2L_CRU_MIN_INPUT_HEIGHT; 296 fmt.format.field = V4L2_FIELD_NONE; 297 fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; 298 fmt.format.colorspace = V4L2_COLORSPACE_SRGB; 299 fmt.format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 300 fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT; 301 fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT; 302 303 return rzg2l_cru_ip_set_format(sd, sd_state, &fmt); 304 } 305 306 static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { 307 .s_stream = rzg2l_cru_ip_s_stream, 308 }; 309 310 static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { 311 .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, 312 .enum_frame_size = rzg2l_cru_ip_enum_frame_size, 313 .get_fmt = v4l2_subdev_get_fmt, 314 .set_fmt = rzg2l_cru_ip_set_format, 315 }; 316 317 static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { 318 .video = &rzg2l_cru_ip_video_ops, 319 .pad = &rzg2l_cru_ip_pad_ops, 320 }; 321 322 static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { 323 .init_state = rzg2l_cru_ip_init_state, 324 }; 325 326 static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { 327 .link_validate = v4l2_subdev_link_validate, 328 }; 329 330 int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) 331 { 332 struct rzg2l_cru_ip *ip = &cru->ip; 333 int ret; 334 335 ip->subdev.dev = cru->dev; 336 v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); 337 ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; 338 v4l2_set_subdevdata(&ip->subdev, cru); 339 snprintf(ip->subdev.name, sizeof(ip->subdev.name), 340 "cru-ip-%s", dev_name(cru->dev)); 341 ip->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 342 343 ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 344 ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops; 345 346 ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK | 347 MEDIA_PAD_FL_MUST_CONNECT; 348 ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE | 349 MEDIA_PAD_FL_MUST_CONNECT; 350 351 ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads); 352 if (ret) 353 return ret; 354 355 ret = v4l2_subdev_init_finalize(&ip->subdev); 356 if (ret < 0) 357 goto entity_cleanup; 358 359 ret = v4l2_device_register_subdev(&cru->v4l2_dev, &ip->subdev); 360 if (ret < 0) 361 goto error_subdev; 362 363 return 0; 364 error_subdev: 365 v4l2_subdev_cleanup(&ip->subdev); 366 entity_cleanup: 367 media_entity_cleanup(&ip->subdev.entity); 368 369 return ret; 370 } 371 372 void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru) 373 { 374 struct rzg2l_cru_ip *ip = &cru->ip; 375 376 media_entity_cleanup(&ip->subdev.entity); 377 v4l2_subdev_cleanup(&ip->subdev); 378 v4l2_device_unregister_subdev(&ip->subdev); 379 } 380