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 .code = MEDIA_BUS_FMT_UYVY8_1X16, 17 .datatype = MIPI_CSI2_DT_YUV422_8B, 18 .format = V4L2_PIX_FMT_UYVY, 19 .bpp = 2, 20 .icndmr = ICnDMR_YCMODE_UYVY, 21 .yuv = true, 22 }, 23 { 24 .code = MEDIA_BUS_FMT_SBGGR8_1X8, 25 .format = V4L2_PIX_FMT_SBGGR8, 26 .datatype = MIPI_CSI2_DT_RAW8, 27 .bpp = 1, 28 .icndmr = 0, 29 .yuv = false, 30 }, 31 { 32 .code = MEDIA_BUS_FMT_SGBRG8_1X8, 33 .format = V4L2_PIX_FMT_SGBRG8, 34 .datatype = MIPI_CSI2_DT_RAW8, 35 .bpp = 1, 36 .icndmr = 0, 37 .yuv = false, 38 }, 39 { 40 .code = MEDIA_BUS_FMT_SGRBG8_1X8, 41 .format = V4L2_PIX_FMT_SGRBG8, 42 .datatype = MIPI_CSI2_DT_RAW8, 43 .bpp = 1, 44 .icndmr = 0, 45 .yuv = false, 46 }, 47 { 48 .code = MEDIA_BUS_FMT_SRGGB8_1X8, 49 .format = V4L2_PIX_FMT_SRGGB8, 50 .datatype = MIPI_CSI2_DT_RAW8, 51 .bpp = 1, 52 .icndmr = 0, 53 .yuv = false, 54 }, 55 }; 56 57 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) 58 { 59 unsigned int i; 60 61 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) 62 if (rzg2l_cru_ip_formats[i].code == code) 63 return &rzg2l_cru_ip_formats[i]; 64 65 return NULL; 66 } 67 68 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format) 69 { 70 unsigned int i; 71 72 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { 73 if (rzg2l_cru_ip_formats[i].format == format) 74 return &rzg2l_cru_ip_formats[i]; 75 } 76 77 return NULL; 78 } 79 80 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index) 81 { 82 if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) 83 return NULL; 84 85 return &rzg2l_cru_ip_formats[index]; 86 } 87 88 struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) 89 { 90 struct v4l2_subdev_state *state; 91 struct v4l2_mbus_framefmt *fmt; 92 93 state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); 94 fmt = v4l2_subdev_state_get_format(state, 1); 95 v4l2_subdev_unlock_state(state); 96 97 return fmt; 98 } 99 100 static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable) 101 { 102 struct rzg2l_cru_dev *cru; 103 int s_stream_ret = 0; 104 int ret; 105 106 cru = v4l2_get_subdevdata(sd); 107 108 if (!enable) { 109 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 110 if (ret) 111 s_stream_ret = ret; 112 113 ret = v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 114 if (ret == -ENOIOCTLCMD) 115 ret = 0; 116 if (ret && !s_stream_ret) 117 s_stream_ret = ret; 118 rzg2l_cru_stop_image_processing(cru); 119 } else { 120 ret = v4l2_subdev_call(cru->ip.remote, video, pre_streamon, 0); 121 if (ret == -ENOIOCTLCMD) 122 ret = 0; 123 if (ret) 124 return ret; 125 126 fsleep(1000); 127 128 ret = rzg2l_cru_start_image_processing(cru); 129 if (ret) { 130 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 131 return ret; 132 } 133 134 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 135 if (!ret || ret == -ENOIOCTLCMD) 136 return 0; 137 138 s_stream_ret = ret; 139 140 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 141 rzg2l_cru_stop_image_processing(cru); 142 } 143 144 return s_stream_ret; 145 } 146 147 static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, 148 struct v4l2_subdev_state *state, 149 struct v4l2_subdev_format *fmt) 150 { 151 struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); 152 const struct rzg2l_cru_info *info = cru->info; 153 struct v4l2_mbus_framefmt *src_format; 154 struct v4l2_mbus_framefmt *sink_format; 155 156 src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); 157 if (fmt->pad == RZG2L_CRU_IP_SOURCE) { 158 fmt->format = *src_format; 159 return 0; 160 } 161 162 sink_format = v4l2_subdev_state_get_format(state, fmt->pad); 163 164 if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) 165 sink_format->code = rzg2l_cru_ip_formats[0].code; 166 else 167 sink_format->code = fmt->format.code; 168 169 sink_format->field = V4L2_FIELD_NONE; 170 sink_format->colorspace = fmt->format.colorspace; 171 sink_format->xfer_func = fmt->format.xfer_func; 172 sink_format->ycbcr_enc = fmt->format.ycbcr_enc; 173 sink_format->quantization = fmt->format.quantization; 174 sink_format->width = clamp_t(u32, fmt->format.width, 175 RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width); 176 sink_format->height = clamp_t(u32, fmt->format.height, 177 RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height); 178 179 fmt->format = *sink_format; 180 181 /* propagate format to source pad */ 182 *src_format = *sink_format; 183 184 return 0; 185 } 186 187 static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd, 188 struct v4l2_subdev_state *state, 189 struct v4l2_subdev_mbus_code_enum *code) 190 { 191 if (code->index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) 192 return -EINVAL; 193 194 code->code = rzg2l_cru_ip_formats[code->index].code; 195 return 0; 196 } 197 198 static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, 199 struct v4l2_subdev_state *state, 200 struct v4l2_subdev_frame_size_enum *fse) 201 { 202 struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); 203 const struct rzg2l_cru_info *info = cru->info; 204 205 if (fse->index != 0) 206 return -EINVAL; 207 208 if (!rzg2l_cru_ip_code_to_fmt(fse->code)) 209 return -EINVAL; 210 211 fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; 212 fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; 213 fse->max_width = info->max_width; 214 fse->max_height = info->max_height; 215 216 return 0; 217 } 218 219 static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, 220 struct v4l2_subdev_state *sd_state) 221 { 222 struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; 223 224 fmt.format.width = RZG2L_CRU_MIN_INPUT_WIDTH; 225 fmt.format.height = RZG2L_CRU_MIN_INPUT_HEIGHT; 226 fmt.format.field = V4L2_FIELD_NONE; 227 fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; 228 fmt.format.colorspace = V4L2_COLORSPACE_SRGB; 229 fmt.format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 230 fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT; 231 fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT; 232 233 return rzg2l_cru_ip_set_format(sd, sd_state, &fmt); 234 } 235 236 static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { 237 .s_stream = rzg2l_cru_ip_s_stream, 238 }; 239 240 static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { 241 .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, 242 .enum_frame_size = rzg2l_cru_ip_enum_frame_size, 243 .get_fmt = v4l2_subdev_get_fmt, 244 .set_fmt = rzg2l_cru_ip_set_format, 245 }; 246 247 static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { 248 .video = &rzg2l_cru_ip_video_ops, 249 .pad = &rzg2l_cru_ip_pad_ops, 250 }; 251 252 static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { 253 .init_state = rzg2l_cru_ip_init_state, 254 }; 255 256 static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { 257 .link_validate = v4l2_subdev_link_validate, 258 }; 259 260 int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) 261 { 262 struct rzg2l_cru_ip *ip = &cru->ip; 263 int ret; 264 265 ip->subdev.dev = cru->dev; 266 v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); 267 ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; 268 v4l2_set_subdevdata(&ip->subdev, cru); 269 snprintf(ip->subdev.name, sizeof(ip->subdev.name), 270 "cru-ip-%s", dev_name(cru->dev)); 271 ip->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 272 273 ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 274 ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops; 275 276 ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK | 277 MEDIA_PAD_FL_MUST_CONNECT; 278 ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE | 279 MEDIA_PAD_FL_MUST_CONNECT; 280 281 ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads); 282 if (ret) 283 return ret; 284 285 ret = v4l2_subdev_init_finalize(&ip->subdev); 286 if (ret < 0) 287 goto entity_cleanup; 288 289 ret = v4l2_device_register_subdev(&cru->v4l2_dev, &ip->subdev); 290 if (ret < 0) 291 goto error_subdev; 292 293 return 0; 294 error_subdev: 295 v4l2_subdev_cleanup(&ip->subdev); 296 entity_cleanup: 297 media_entity_cleanup(&ip->subdev.entity); 298 299 return ret; 300 } 301 302 void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru) 303 { 304 struct rzg2l_cru_ip *ip = &cru->ip; 305 306 media_entity_cleanup(&ip->subdev.entity); 307 v4l2_subdev_cleanup(&ip->subdev); 308 v4l2_device_unregister_subdev(&ip->subdev); 309 } 310